The line:
a[i] = strtok(s," ");
sets a[i]
to a pointer within the string, which you’re modifying and which may eventually have the temporary NUL characters (from strtok
) removed.
You may want to try:
a[i] = strdup (strtok (s," "));
instead.
Let me explain further. Say we have the command (as it exists in memory):
ls -al xyzzy\0
When you strtok
it, it almost certainly gets changed to:
ls\0-al xyzzy\0
^
and then it gives you s
(indicated by ^
) returned from strtok
and placed into a[0]
. The next time through, it changes the string to:
ls -al\0xyzzy\0
^
and gives you the address indicated by ^
and places that into a[1]
. Unfortunately, a[0]
now points to the string "ls -al"
Eventually, the string will return to it’s original state and your pointers, while pointing to the right addresses, won’t have strings null-terminated as you expect (except for the last one).
So you’ll end up with:
a[0] = "ls -al xyzzy"
a[1] = "-al xyzzy"
a[2] = "xyzzy"
By using strdup
, you make a copy of each string (at the time when the string is as you expect, a single word), which isn’t then modified by later operations in the string tokenising code.
Remember, however, to free all those strings you’ve allocated when you’re done with them.
You can use this code as a baseline:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main (void) {
char str[256], *word[20];
int i, num, len;
printf ("Enter command: ");
fgets (str, sizeof (str), stdin);
num = 0;
word[num] = strtok (str, " ");
while (word[num] != NULL) {
word[num] = strdup (word[num]);
len = strlen (word[num]);
if (strlen (word[num]) > 0)
if (word[num][len-1] == '\n')
word[num][len-1] = '\0';
word[++num] = strtok (NULL, " ");
}
for (i = 0; i < num; i++) {
printf ("%d: [%s]\n", i, (word[i] == NULL) ? "<<null>>" : word[i]);
free (word[i]);
}
return 0;
}
and the following transcript shows it in action:
Enter command: ls -al xyzzy | grep -v plugh
0: [ls]
1: [-al]
2: [xyzzy]
3: [|]
4: [grep]
5: [-v]
6: [plugh]
Additionally, the correct way to compare C strings is not:
if (a[0] = "cd")
since that compares addresses, which may be different even if the backing content is identical. You should instead use something like:
if (strcmp (a[0], "cd") == 0)
And one final thing, the argv[]
array that you pass to execvp
must have a final terminating NULL pointer. That’s part of the contract, so you’ll need to ensure that gets put in before calling execvp
.
1
solved Create linux environment in C using execl and fork() [closed]