1

I'm looking to get the absolute path of a binary (specifically 7zip's 7z). I think this could be done by either

  1. getting the $PATH environment variable, and checking existence of 7z in each of the directories, or

  2. "shelling out" to the which command and reading from its stdout.

Which method is the most conventional?

moltenform
  • 793
  • 7
  • 13
  • I'd tend to `which`. AFAIK, it's `$PATH` in *nix anyway and why use that self-built method if there's even a program specifically for that purpose? – cadaniluk Nov 21 '15 at 20:29
  • Please explain and motivate why you need to get the absolute path of a `7z` binary; I am guessing it is wrong (or very unusual, unless you are coding a shell and its program hash table) – Basile Starynkevitch Nov 21 '15 at 20:46
  • The idea was to let the user specify the absolute path to the binary in a configuration file, and to provide a default value based on $PATH. But I think I'll reconsider this design based on your information below, probably to use execvp(3) by default unless the configuration file overrides this. Thank you! – moltenform Nov 21 '15 at 21:03
  • `execvp` will do what you expect if passed an absolute program path like `/usr/local/bin/which` so you can *always* use `execvp` without checking anything. – Basile Starynkevitch Nov 21 '15 at 21:04
  • Why your program needs `7z` ? It is a quite unusual requirement and I suggest you to avoid needing it – Basile Starynkevitch Nov 21 '15 at 21:25

1 Answers1

5

The first method (using getenv("PATH") and scanning it....) is preferable and more efficient... This is often what which usually does (but don't depend on which whose behavior can vary a lot...) but you won't fork(2) any process (and fork or system or which may fail).

However, I question why you need to find the path of some executable. You could simply use execvp(3) (and similar exec*p* functions, all of them doing the equivalent of getenv("PATH"), then scanning it like you would, internally before execve(2)) when executing that program.

(In very pathological cases which you are right to ignore, the getenv("PATH") could fail)

Notice that the binary executable could be removed (or added, or corrupted...) between the moment when you test its existence (and find it in PATH) and the moment you are execve(2)-ing it. And that execve can fail (rarely) for many reasons, or not work as you expect (in particular, if some shared library on which your 7z executable depends is corrupted or removed).

Don't forget that on Linux, several processes could read and write (or execute) the same file (which could also be removed or renamed while it is read). Please read more about i-nodes.

Hence, finding the full path of 7z before execvp-ing it is wrong -or inaccurate: some other program could have removed or reinstalled 7z after the path check and before its execve(2). I don't understand why you want to scan the PATH, and I believe it is generally useless, leave that to execvp

See also wordexp(3), glob(3), glob(7)

Since most software on Linux distributions are free software, you can (and perhaps you should) study their source code. For which it is often a builtin of your shell (and bash, zsh, fish are all free software), or a program from debianutils (perhaps not coreutils), and I am not sure that most distributions have it (and some of them won't install it by default).

PS. My feeling is that your code should not use 7z which is usually not available on most Linux systems - and not on my systems; you are perhaps implicitly requiring your user to install it (at least document that). You might prefer tar archives to zip ones, notice that tar.h is a POSIX header! See this question and consider libtar

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • I could have sworn there was a standard library routine to do the path search of `execvp` (etc) but not do the `exec` but I can't seem to find it. – abligh Nov 21 '15 at 20:34
  • AFAIK, not in the standard library – Basile Starynkevitch Nov 21 '15 at 20:35
  • I added a link to the `which` source code. Hope that's OK. It bears out your point. – abligh Nov 21 '15 at 20:36
  • 1
    You link is to some apple code, not some GNU code. So I removed the link – Basile Starynkevitch Nov 21 '15 at 20:45
  • @BasileStarynkevitch Uh? Why? – marko Nov 21 '15 at 20:52
  • Because the question is tagged Linux, and its `which` is generally from coreutils, not from Apple code (at least on most distributions) – Basile Starynkevitch Nov 21 '15 at 20:53
  • The OP is likely after an approach rather than a code to copy and paste. Seeing as both Linux and Darwin are POSIX complaint systems, and the implementation of `which` on OS X is almost certainly from BSD anyway, it's probably entirely relevant. – marko Nov 21 '15 at 20:57
  • @marko feel free to add a comment with example code – poolie Nov 21 '15 at 21:00
  • In many shells, `which` is a builtin – Basile Starynkevitch Nov 21 '15 at 21:01
  • 1
    @BasileStarynkevitch: `which` is not from coreutils. On Debian-based systems, it's part of the `debianutils` package. On CentOS (based on Red Hat), it's provided by the `which` package. On some systems, `which` attempts to search for shell aliases. (For interactive use, if you're using a Bourne-based shell such as bash, `type` is probably better.) – Keith Thompson Nov 21 '15 at 21:12
  • 1
    @BasileStarynkevitch: In csh, tcsh, and zsh; not in bash or ksh. I've found that the behavior of `which` varies so much that it's not really worth using. In bash, `type` is more reliable. In tcsh, the built-in `where` gives more information. – Keith Thompson Nov 21 '15 at 21:16
  • @BasileStarynkevitch I put the BSD licensed code in as a link as it was the more permissive licence. It should still work on Linux. – abligh Nov 22 '15 at 10:04