Shell Analysis

· jswank's blog

#sh #bash

I have an old saved tab with the MUSL configure script. A few things struck me on first glance- like the fact it is written using sh rather than bash, and it has a noticeably terse style. But I had to take a closer look at the argument parsing logic!

# The For Loop

https://github.com/bminor/musl/blob/master/configure#L147

1for arg; do
2 ...
3done

How is arg being set?

This is a terse form of a for loop in shell. It can also be written more verbosely to a familiar form:

1for arg in "$@"; do
2 ...
3done

So, arg is set to each argument supplied to the script.

# Argument Parsing

https://github.com/bminor/musl/blob/master/configure#L14A8

1case "$arg" in
2  --help|-h) usage ;;
3  --srcdir=*) srcdir=${arg#*=} ;;
4  --prefix=*) prefix=${arg#*=} ;;
5  ...
6  --enable-shared|--enable-shared=yes) shared=yes ;;
7  ...
8  *) build=$arg ; target=$arg ;;
9esac

What is srcdir or prefix being set to?

If arg is --srcdir=/some/path, the variable srcdir is set to '/some/path`.

How does ${arg#*=} work?

This is a variety of parameter expansion that is in POSIX shell (not just bash). The specification says:

${parameter#[word]}

Remove Smallest Prefix Pattern. The word shall be expanded to produce a pattern. The parameter expansion shall then result in parameter, with the smallest portion of the prefix matched by the pattern deleted. If present, word shall not begin with an unquoted '#'.

In the case above, word is '*='. As a shell pattern matching expression, that means zero or more characters ('*') followed by an equals sign.

1$ ( arg='undergrad'; v=${arg#*g}; echo "$v" )  # delete everything through the first "g"
2rad
3$ ( arg='foo:bar'; v=${arg#*:}; echo "$v" )  # delete everything through the first ":"
4bar

# References