Search code examples
cexecexecvpexecv

understanding why`execvp` works where `execv` fails


I was asked to implement a 'mini-shell' in C. I decided to work with execv and it didn't work, and when I changed it to execvp its working! Take a look of the code (the action is in tokExec function)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>


void tokExec(char command[128])
{
    // 'strtok' may look innocent, but it modifies the string, so I want a copy of the original command.
    char tokString[128];
    // 'strlen' is the lenghth without the null terminator.
    strncpy(tokString,command,strlen(command)+1);


    char* tokPtr = NULL;
    char tok[] = " ";
    int narg = 0;
    tokPtr = strtok(tokString,tok);
    char *myargs[64];

    while(tokPtr != NULL)
    {
            printf("arg %d is: %s\n",narg,tokPtr);
            myargs[narg] = tokPtr;
            narg = narg + 1;
            tokPtr = strtok(NULL,tok);
    }
    printf("Total number of arguments: %d\n",narg);

    // add the final 'NULL' element.
    myargs[narg] = NULL;

    execvp(myargs[0],myargs);
    printf("error\n");
    exit(1);
}

void normal()
{
    char command[128];
    strcpy(command,"default\0");
    printf("myShellZ > ");
    gets(command);
    while(strcmp(command,"exit") != 0)
    {
            int status;
            pid_t pid;
            if( (pid = fork()) == 0 )
            {
                    tokExec(command);
            }
            wait(&status);
            printf("myShellz > ");
            gets(command);
    }
}

void debug()
{
    // TO DO ....
    int a = 3;
}

// switching between shell modes: normal or debug.
int main(int argc, char* argv[])
{
    if(argc == 1)
            normal();
    else if(strcmp("-debug",argv[1]))
            debug();
    else
            exit(1);

    exit(0);
}

If instead of the execvp in the end of tokExec I will use execv, if my input is only ls or ps etc execv is great, but if I add arguments to the input, for example: ls -l myshell.c or evev just ls -l or ps aux I got an error output.

The man barely refer to the differences between those functions, but it claims that:

The execv(), execvp(), and execvpe() functions provide an array of pointers to null-terminated strings that represent the argument list available to the new program. The first argument, by convention, should point to the filename associated with the file being executed. The array of pointers must be terminated by a null pointer.

In conclusion, what's the difference betwwen execv and execvp that fixed my program in this case? I know that execv also works for bash commands since if I entered bash command without arguments it is working, and the signature of those two functions is identical. Thanks for any help!


Solution

  • The answer is in the man page. If you read the section after the part you've quoted, it talks about the difference

    The execlp(), execvp(), and execvpe() functions duplicate the actions of the shell in searching for an executable file if the specified filename does not contain a slash (/) character. The file is sought in the colon-separated list of directory pathnames specified in the PATH environment variable. If this variable isn't defined, the path list defaults to the current directory followed by the list of directories returned by confstr(_CS_PATH). (This confstr(3) call typically returns the value "/bin:/usr/bin".)