2

I'm writing a wrapper script to wrap around the execution of another command; in this case the Chef kitchen command. My script will also be called kitchen and put earlier in $PATH so that running kitchen from bash will run my wrapper script instead.

The question is: how do I call the original version of kitchen? The obvious way, of course, is just to give the full path — just put /usr/bin/kitchen in the script, but someone else may have it installed at a different path. And of course that precludes any other wrapper scripts—it'd be nice if the solution were stackable.

Two approaches come to mind, both variants on the same theme:

  1. In the script, go through $PATH. Compare each entry to $(dirname "$0") using stat using device number & inode number to see if its the same directory. If so, remove it from $PATH. Then can just call kitchen, because it shouldn't re-call the wrapper script.
  2. Similar to #1, but do the $PATH lookup by hand in the script, and use stat on each result vs. $0. Keep skipping until we find ourself, then use the next one found. If we never find ourself, then just exec it normally (to handle the case the wrapper isn't in $PATH but was executed by providing a path to it)

Is there a better way? Or does code to do this already exist somewhere on a typical Debian or Ubuntu Linux system?

5
  • Hey derobert long time no see!
    – jesse_b
    Sep 22, 2021 at 18:53
  • 1
    If your wrapper script uses bash, then you can use type -a to find all the places, then you can use the second. For a stackable solution you need to look through the list and choose the one after yourself.
    – icarus
    Sep 22, 2021 at 18:56
  • @icarus could combine that into #2 and that'd be not that bad.
    – derobert
    Sep 22, 2021 at 19:06
  • @zevzek those indeed make jesse_b's simple alias solution look even better... honestly, you should post that as an answer. "You can't, because..." is a perfectly good answer to "How do I...?"
    – derobert
    Sep 22, 2021 at 19:53
  • A variant of #2 which does the same inode check as #1 would avoid the infinite loop problem (unless you somehow end up with two versions of the script in different locations in the path...). But it all does feel like adding quite a bit of complexity to handle the stackability requirement. Sep 23, 2021 at 9:40

3 Answers 3

1

I've done something similar a few times in the past and what I'll do is just use command kitchen in the script, name it something else like my-kitchen and then create an alias:

alias kitchen=/path/to/my-kitchen

Not sure if that works for your use case though.

1
  • Aliases are only expanded in interactive shells anyway (unless you set expand_aliases in bash at least), so don't even need to use command. Can't stack it easily, but especially with the problems zevzek will hopefully turn into an answer — your "have you tried the simple approach?" seems the right way to go.
    – derobert
    Sep 22, 2021 at 20:03
1

You can escape an alias with a backslash to prevent it from being expanded by the shell. command also works.

I don't have kitchen on my desktop, so I'll demonstrate by aliasing pwd to uname -r :

$ alias pwd='uname -r'
$ pwd
5.10.0-8-amd64
$ \pwd
/home/cas
$ command pwd
/home/cas
$ unalias pwd
$ pwd
/home/cas
0

It sounds like you want to execute the next kitchen in $PATH.

#!/bin/bash
me="$0" 
foundit=""
for d in $( echo "$PATH" | tr ":" " " ) ; do
  if [[ $foundit  == "" ]] ; then
    if [[ "$me" != "$d/kitchen" ]] ; then
      if [[ -x "$d/kitchen" ]] ; then
        foundit="$d/kitchen" 
      fi
    fi
  fi
done
# the next "kitchen" is in "`$foundit`"
# read `man bash`, about the `eval` and `exec` builtins
2
  • 1
    That's a version of #2, but using file name to compare instead of inode number. I suspect you've got a few bugs there (e.g., $0 will often just be kitchen, so it won't match). realpath could deal with those, but it has the same limitations as zevzek pointed out in their comment.
    – derobert
    Sep 22, 2021 at 20:45
  • or maybe IFS=: read -a path <<< "$PATH" and for d in "${path[@]}"; do... to avoid the usual whitespace issues. Which, admittedly, you're not likely to see in PATH, but you only need one joker to use a directory with spaces in the name...
    – ilkkachu
    Sep 23, 2021 at 11:54

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.