TIL: bash arguments

Today I learned a bit about bash shell script command line arguments. I’d known some/most of this for some time, but did a little deeper dive, based on a PR I saw at work that had some code in it I had never seen before.

Processing “Groups”

I haven’t looked into the “why” yet (and honestly I don’t care that much), but bash has both $* and $@ to get your command line args into your script. I’d looked into this before, but there are subtle differences when iterating over them in your script, especially if you have whitespace between them, if they are within quotes, and how you want them represented in your script.

TL;DR: use "$@" for most cases.

For example, if you run a script with bash test.sh "foo bar" baz, you PROBABLY want "foo bar" to be one argument, since it’s quoted as such on the command line, and then of course "baz" to be a second argument. Various combinations of $* and $@, with and without quotes around them in your script will do different things. Here’s the rundown. Code link for this test script at the end.

❯ bash ./test.sh "foo bar" baz
I got 2 args

$*
Argument: [foo]
Argument: [bar]
Argument: [baz]

"$*"
Argument: [foo bar baz]

$@
Argument: [foo]
Argument: [bar]
Argument: [baz]

"$@"
Argument: [foo bar]
Argument: [baz]

et voila!

Extracting Specific Arguments

Dealing with arrays in bash has always been a bridge too far for me (reasonable people can disagree), and if I ever get into a situation where I absolutely need that I’ll probably write the script in ruby, perl, python, or some other language that handles them natively a bit more in line with my experience.

That said, occasionally you need to pluck out a specific positional value from the bash command line args, and TIL the easy way to do that.

The syntax is ${@: <index>:<size>}. So if you want the first argument from the list of args, it’s ${@: 1:1} (or just ${@: 1} as the size appears to default to 1.)

You can also use a negative index to count from the END of the arg list, so if you want the last argument, ${@: -1:1} (or, again, ${@: -1}) using the default.

Note that index 0 is the script/file name itself, in line with the C language argv convention.

The space after the @: appears to be significant.

Code

Github Repo