2

There's an executable /usr/bin/foo which I and other scripts use but it misbehaves a bit so I made a Bash wrapper of the same filename in /usr/local/bin/foo where I fixed its misbehaviour. My PATH is /usr/local/bin:/usr/bin. In the wrapper script, I have to run the original executable by absolute path in order to not get into an infinite loop:

$ cat /usr/local/bin/foo
#/bin/zsh
/usr/bin/foo | grep -v "^INFO" # stfu, really

Is there any (Zsh or Bash specific perhaps) straightforward way to execute next foo in PATH, that is the foo that's in PATH directories that are after the directory from which current foo was executed, so that I wouldn't have to use absolute path to the original executable?

I could make a function for it in /etc/zshenv and it's not such a big deal. I'm just wondering if there is anything standard. I don't want to use alias or fix the original executable.


EDIT 1:

$ cat /usr/local/bin/foo
#/bin/zsh
path=(${path/#%$0:A:h}) foo | grep -v "^INFO" # stfu, really

This should empty (but keep) all strings in PATH (${path/...}) that fully match (#%) the absolute (:A) directory (:h) of the current executable($0). TBH, I assembled it from StackExchange and don't understand it fully. I hope it's not going to bite me.

EDIT 2:

$ cat /usr/local/bin/foo
#/bin/zsh
path[${path[(i)$0:A:h]}]=() foo | grep -v "^INFO" # stfu, really

$path[(i)foo] finds the index of foo in the array path or 1 plus length of the array.

  • 1
    I imagine your wrapper script self-mangling PATH by removing its parent directory before calling foo again – Jeff Schaller Mar 26 '16 at 21:55
  • @JeffSchaller Thanks. You're right. I updated the question. – woky Mar 26 '16 at 22:17
  • It could bite you if the directory happens to be a substring of some other directory name. It's probably good enough for personal use, but not for code that you expect others to use. – Thomas Dickey Mar 26 '16 at 22:34
2

You can find out what directory the script is in ($0:h in zsh, "${0%/*}" in sh) and remove that directory from PATH (path=(${path:#$0:h}) in zsh, more complicated in sh. This could fail if PATH contains the same directory twice, e.g. through a symbolic link.

A downside of the straight approach is that this removes the directory from the path, but other programs in the same directory might be desirable. You can solve this issue by doing only the path lookup with a modified path.

next=$(path=(${path:#$0:h}); print -lr -- =$0:t)
$next

Instead I'd do the PATH lookup manually and skip any occurrence of the running script.

for d in $path; do
  if [[ -x $d/$0:t && ! $d/$0:t -ef $0 ]]; then
    exec $d/$0:t
  fi
done
1

Example:
For me type -a egrep prints a user-defined alias and the actual egrep command, adding yet another egrep in my $HOME/bin/ shows it too... In the order; alias first, then the remaining items in the same order as PATH has them.

$ cd # go home

$ mkdir -p bin

$ PATH=$HOME/bin:$PATH

$ type -a egrep
egrep is aliased to `egrep --color=auto'
egrep is /bin/egrep

$ echo -e >bin/egrep '#!/bin/bash\necho TEST'

$ chmod 755 bin/egrep

$ type -a egrep
egrep is aliased to `egrep --color=auto'
egrep is /home/$USER/bin/egrep
egrep is /bin/egrep

# assuming you're sure it is the only one not in /home and not an alias
$ type -a egrep | grep -Ev '/home|alias' | cut -d' ' -f3
/bin/egrep

$ rm $HOME/bin/egrep

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.