3

I'm writing a shell in C, and I'm having trouble understanding the filepath parameter needed for execvp(filepath,argv).

If the user typed wanted to run ls -a in their current directory ... let's say /home/user1 ... what would be the filepath and argv for running ls in said directory?

Would the filepath be the directory for where the command will be executed from /home/user1 or would it be the command's location /bin/ls?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Peter Chappy
  • 1,165
  • 4
  • 22
  • 41

2 Answers2

6

The filepath argument to execvp() can take either of two forms — it contains a slash or it does not contain a slash.

With slash

The filepath argument specifies the pathname of the executable, either an absolute name (starts with slash) or a relative name (does not start with slash, but does contain a slash). The execvp() function makes no changes to the argument, and the program is executed (or not) assuming that the file named exists and is executable.

Without slash

The filepath arguments specifies a 'simple' name such as ls. The excevp() function then seeks to execute a program with the name ls found in one of the directories listed in the $PATH environment variable. The p in execvp() is for 'path', as in path lookup.

Applying the theory

If the user types ls -a to a shell, the normal way to execute that would be to create an array of character pointers equivalent to:

char *argv[] = { "ls", "-a", 0 };

execvp(argv[0], argv);

The execvp() will now do the path-based analysis and attempt to execute ls from one of the directories listed in $PATH.

If the user types /bin/ls -a to a shell, then the normal way to execute that would be to create an array of character pointers equivalent to:

char *argv[] = { "/bin/ls", "-a", 0 };

execvp(argv[0], argv);

The execvp() will now execute the absolute pathname specified, because that's what the user requested (as opposed to, say, /usr/bin/ls or /usr/local/bin/ls).

Note that the processing is actually the same — you split the command line into words; each word becomes an element of an array of character pointers that is terminated with a null pointer, and you pass the first word to execvp() as the 'filepath' argument, and the whole array as the second argument.

Obviously, a shell can cache the locations of the actual executables, and many shells do, so that execvp() doesn't have to do work trying to find the program (and the shells don't call execvp() but typically call execv() with the absolute pathname of the executable. But that isn't necessary; it is an optimization.

Note, too, that there is nothing to stop you doing:

char *argv[] = { "/honky/tonk/toys", "-a", 0 };
execvp("ls", argv);

Now argv[0] should be "/honky/tonk/toys" and not ls, for all it is the ls executable that is run. What you find in /proc depends on you having /proc on your system (Mac OS X does not support it, for example), but the symlink to the binary should be a link to /bin/ls. On Linux, you're apt to find that ps reports the binary name (ls), even though /proc/PID/cmdline contains the original arguments (so argv[0] is /honky/tonk/toys). Whether this is good or not depends on your viewpoint, but all the world is not Linux.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    does that means that for executing a file in current directory we have to give "./filename" as the file path – Gaurav Gupta Feb 28 '16 at 15:58
  • 1
    If you don't have the current directory on your `$PATH` (either explicitly as `.` or via an absolute pathname, or implicitly by a leading or trailing colon or adjacent colons in the value of PATH), then you need to specify `./filename` to execute the program `filename` in the current directory via `execvp()`. A plain `execv()` would execute the file in the current directory without the leading `./`, though (but wouldn't find `filename` anywhere else). Example paths with current directory: `PATH=:/bin`, `PATH=/bin:`, `PATH=/bin::/usr/bin`, `PATH=/bin:.`. (There are other arcane possibilities.) – Jonathan Leffler Feb 28 '16 at 16:22
  • How does it look for PATH without `envp`? And what if `envp` is changed in the current process? – user129393192 Apr 19 '23 at 02:57
  • @user129393192: The fallback position is to use `getenv("PATH")` — but there is a decent chance that the implementation of `execvp()` is privy to some internal data from the library and can do things differently. And a shell may well not use the system-provided `execvp()` but use its own variant which allows it to know and cache the path to an executable, which it will use next time unless the cache had to be invalidated. – Jonathan Leffler Apr 19 '23 at 04:09
1

To run /bin/ls -a in the current directory you would need:

The same as argv passed into main() when a C program starts except NULL terminated, and path is the same as argv[0].

char* argv[] = { "/bin/ls", "-a", NULL };
execvp(argv[0], argv);
goji
  • 6,911
  • 3
  • 42
  • 59
  • One other question after execvp runs like in your example will it automatically print results to terminal or will I have to do something else too? – Peter Chappy Oct 06 '13 at 04:34
  • It will write to stdout / stderr. So yes you'll see it on terminal. – goji Oct 06 '13 at 04:36