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.