Understanding ${0} ## what's $0 Classic unix shells store whatever variables you pass them, or their scripts, in a special list accessable as $@ or just numbers: $1, $2 etc. Take this program: #!/bin/sh echo "${@}" That's what we call a wrapper, it's pretty much same as calling echo. > $ ./u0.sh foo bar # outputs `foo bar` You'll quickly note these are indexed starting from 1. $0 is a special variable, describing whatever the current command is running in. Try this: > $ echo $0 # for me on sdf, outputs `/usr/pkg/bin/bash` I don't run screen or anything, or start any shells after logging into sdf, so it's the exact value of my login shell. But let's try starting a new bash shell and running it there: > $ bash > $ echo $0 # outputs `bash` $0 describes exactly what the current shell was invoked as. This is useful in executable scripts, where you can reference the name of the program: #!/bin/sh echo my name is $0 It's the exact path used: > $ ./t.sh # outputs `my name is ./t.sh` ------------------------------------------------------------------------ ## recursion This is useful for recursion. A common pattern to easily make a program chew through directories is this: #!/bin/sh # for loop with no list given will just go over $@ for i do if [ -f "$i" ] then # Actual runtime echo "$i" elif [ -d "$i" ] then # Call the program again on content of directories "$0" "$i"/* fi done Note that this is prone to errors, more of a hack than anything: - If the right shell option isn't set, this \* glob will omit dotfiles - When `cd` gets involved, if the program was invoked with a relative path (like `./t.sh`, as opposed to `t.sh` or the full path `/home/morus/bin/t.sh`), this won't work as expected. Also notable, the $0 stays the same inside functions - referencing the program name, not the function name. #!/bin/sh echo "outside $0 $1 $2" wrapper() { echo "inside $0 $1 $2" } wrapper asdf wsad wrapper "$@" The $@ list gets replaced inside functions, which is why you often need to pass the $@ of outer scope to them. But remember that $0 stays the same - use the explicit function name if you want to make it recursive. > $ ./t.sh foo bar > outside ./t.sh foo bar > inside ./t.sh asdf wsad > inside ./t.sh foo bar ------------------------------------------------------------------------ ## link magics A neat trick is to use $0 and symlinks for branching. Say you have a program that's a tiny wrapper for tar(1): #!/bin/sh # t - tiny tar wrapper tar cv "$1" > "$1".tar Now let's say sometimes you want to do something similar, but also compress the archive with xz(1) and save it to your home directory. You could maintain another script, or write some code for parsing options to invoke it like `t -b somedirectory`. Or you could do something like this: #!/bin/sh # t - tiny tar wrapper PROGNAME="${0##*/}" # get basename of this program pack() { tar cv "$1" } if [ "$PROGNAME" = archive ] then pack "$1" | xz > "$HOME"/"$1".tar.xz else pack "$1" > "$1".tar fi Then create a symlink to this script named `archive`, and whenever you invoke it like that it'll branch to the other behaviour. Easy to remember, easy to modify and maintain, just a bitch to distribute and move.