Been stuck for hours at this. How do I read in command line arguments in my command prompt program and execute it using execv() system call? The following is the sample output where YWIMC is the prompt.
YWIMC > R /bin/ls
a.out ex2.c ...... //output from the “ls” command
YWIMC > R /bin/ls –l //same as executing “ls –l”
total 144
-rwx------ 1 sooyj compsc 8548 Aug 13 12:06 a.out
-rwx------ 1 sooyj compsc 6388 Aug 13 11:36 alarmClock
.................... //other files not shown
An R command has the syntax R command_path [arg1 to arg4] where they could be 0 to 4 arguments. Eg R /bin/ls OR R /bin/ls -l
I'm supposed to use execv (I'm assuming its better for reading command line arguments since it uses a char array as its parameter and also cos my homework assignment requires me to) but I'm having trouble reading the arguments in.
How do I do this when there are any amount of arguments (0 to 4)? While reading in arguments, how do I make the program recognise that that is the end of all the arguments I gave? (I had a problem where I would add an infinite number of arguments even though I set the max to 4.) The following was my existing code just that I have to change execl to execv
else {
result = fork();
if(result != 0) { //Parent Code
childID = wait(&status);
}
else { //Child Code
if(strcmp(filePath, "/bin/ls") == 0) { //specific /bin/ls command
execl("/bin/ls", "ls", NULL);
}
else { //run other programs
execl(filePath, NULL);
}
return 256;
}
You can create an array of strings using malloc
. As you read in arguments, use realloc
to increase the size of the array. Then you can pass the resulting array to execv
.
int arglen = 1;
char **args = malloc(arglen * sizeof(char *));
args[0] = strdup(/* program to run */);
while (/* more args to read */) {
arglen++;
args = realloc(args, arglen * sizeof(char *));
args[arglen-1] = strdup(/* next arg */);
}
arglen++;
args = realloc(args, arglen * sizeof(char *));
args[arglen] = NULL;
...
execv(/* prog to call */, args);
the idea i have in mind is to read file/filepath, take in all arguments, use execv(program,args).
You are on the right track. The exercise not only focuses on the use of execv
, but subtly focuses on the command line argument indexes where you must pay attention to the fact that the first argument given on the command line has and index of 1 (eg argv[1]
), while the array needed to hold the arguments to pass to execv
will begin at index 0
.
Handling the index offset isn't too difficult, you just have to pay attention to how you fill the array you will send to execv
. NOTE : variable initialization is important. The args
array you send to execv
must have the final pointer be NULL
to serve as a sentinel for execv
to know where to stop processing arguments. One way to insure you provide a NULL
pointer at the end of the array is to initialize ALL pointers to NULL
to begin with:
char *args[5] = {NULL};
Then your only task is to insure you only fill the first 4 pointers in the array, leaving the last NULL
. Also note , the first pointer in args
must contain the full-path to the program you are executing. Your original example above showing R ls
will not work unless you append the proper path to the beginning of ls
(eg /usr/bin/ls
). How you do that is up to you. With that a short example is:
#include <stdio.h>
#include <unistd.h>
int main (int argc, char **argv) {
char *args[5] = {NULL}; /* all pointers initialized to NULL */
int lim = argc < 5 ? argc : 5; /* limit the number of args to read */
int nargs = lim - 1; /* adjust the index to fill args */
int i;
/* read the arguments from the command line */
for (i = 1; i < lim; i++)
args[i-1] = argv[i];
/* output the arguments read */
for (i = 0; i < nargs; i++)
printf (" args[%d] = %s\n", i, args[i]);
/* call execv (NOTE: you must provide a full-path
to the program being executed, e.g. /usr/bin/ls)
*/
if (execv (args[0], args) == -1)
fprintf (stderr, "error: execv failed - path correct?.\n");
return 0;
}
Compile
$ gcc -Wall -Wextra -o bin/execvargs execvargs.c
Use/Output
$ ./bin/execvargs /usr/bin/ls
args[0] = /usr/bin/ls
3darrayaddr.c getline_minimal.c reallocptr.c
BoggleData.txt getline_rdfile.c rec_char_in_str.c
<snip>
$ ./bin/execvargs /usr/bin/ls -l
args[0] = /usr/bin/ls
args[1] = -l
total 7528
-rw-r--r-- 1 david david 376 Sep 23 2014 3darrayaddr.c
-rw-r--r-- 1 david david 192 Jun 27 01:11 BoggleData.txt
-rw-r--r-- 1 david david 3565 Jun 26 2014 DoubleLinkedList-old.c
<snip>
$ ./bin/execvargs my dog has fleas and so does the cat
args[0] = my
args[1] = dog
args[2] = has
args[3] = fleas
error: execv failed - path correct?.
Looking at execv a little closer
If you would like to see how execv
invokes the program you pass as args[0]
you can create a small test script in your shell that will echo the arguments it recieves when invoked by execv
. Something simple is fine:
#!/bin/bash
declare -i cnt=0
for i in "$@"; do
printf " arg[%d] : %s\n" $cnt "$i"
let cnt=cnt+1
done
Call it test.sh
and make it executable. (eg chmod 0755 test.sh
) Then provide ./test.sh
along with arguments of your choosing to your program:
$ ./bin/execvargs ./test.sh my dog has fleas
args[0] = ./test.sh
args[1] = my
args[2] = dog
args[3] = has
arg[0] : my
arg[1] : dog
arg[2] : has
According to the comments, the main problem is NOT the actual execution but the parsing of a command line in your own code. Therefore, I'm just giving the most basic solution here I can think of, heavily commented. It doesn't support anything like quoting, escaping, or editing of the command line by the user, but it should help understand the general concept -- try to use it as a starting point.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define PROMPT "YWIMC > "
/* read in a command */
static char **getCommand(void)
{
/* static buffers, no need to allocate dynamically for a simple example */
static char cmdline[1024]; /* command-line */
static char *command[7]; /* parsed command */
/* initialize empty command */
memset(command, 0, sizeof(command));
do
{
/* show prompt: */
fputs(PROMPT, stdout);
/* read commandline: */
if (fgets(cmdline, 1024, stdin))
{
int i = 0;
/* get first token (the command name) */
if ((command[i++] = strtok(cmdline, " \r\n\t")))
{
/* get up to 5 additional tokens (the parameters) */
while (i < 6 && (command[i++] = strtok(0, " \r\n\t")));
}
}
} while (!(command[0])); /* until something was entered */
return command;
}
int main(void)
{
/* endless loop */
while (1)
{
/* get next command */
char **command = getCommand();
/* and try to execute it */
if (!strncmp(command[0], "Q", 1))
{
/* quit command */
puts("k tnx bye.");
exit(0);
}
else if (!strncmp(command[0], "R", 1))
{
/* run command */
/* check there was something to run given: */
if (!command[1])
{
fputs("Command `R': Nothing to run.\n", stderr);
}
else
{
/* YOUR COMMAND EXECUTION GOES HERE */
/* (try to create another "static" function for it) */
}
}
else
{
int i = 0;
/* some debugging on unrecognized commands */
fprintf(stderr, "Unrecognized command: `%s' (args: ",
command[i++]);
while (command[i])
{
fprintf(stderr, "`%s' ", command[i++]);
}
fputs(")\n", stderr);
}
}
}
A key issue to understand is why command
has 7 entries. The first one is reserved for your command. the last one should always be NULL
(or 0
) so your code can detect the end of the arguments list. Leaves 5 usable parameters. If one of your command should run the first of it's parameters as an external command and allow 4 additional arguments, you need exactly those 5.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.