Bash For Arg
Korey Hinton onshell scripting, tested in Bash 5.0.16(1)-release (x86_64-pc-linux-gnu), on grml 2020.06 (VirtualBox VM)
Argument Loop, my definition
This article demonstrates looping for/while-statements over passed-in arguments via whitespace-delimited (quote-escaped) words that follow the invocation command.
I do not consider the invoking command to be an argument. If you want the invoking command use "$0" or to get the whole command line as a string use "$@".
"For Arg" is a Quick and Repeatable Looping Method
I haven't seen any documentation about this, so keep that in mind. I discovered it in a shell script found in /bin/bzdiff of my grml live instance. This is so easy to use and memorable, it almost seems too good to be true. Unless I'm missing something destructive about this, it appears to be somewhat of a hidden scripting gem. I've seen other tutorials where you reconstruct the args by indexing off "$@", that seems like overkill now that I learned how to do it this way.
print-args.bash
#!/bin/bash for ARG; do echo "$ARG" done
command line
chmod +x print-args.bash ./print-args.bash 1 "2" 'three' "4th arg" '5th arg'
output
1 2 three 4th arg 5th arg
Simple enough? I've explained this in greater detail in the Additional Notes section below.
While Shift Method (not repeatable)
Here is a completely different way to enumerate the arguments, using a while loop and shift. I discovered this method in grml code, in a check4progs function (checks for program names existing in each directory specified in PATH).
This is a very efficient way to process arguments, and I assume the arguments are cleared out of memory on each argument shift. Use this when you know you only need to iterate in one pass for efficiency. Shift will move all arguments left 1 spot and the left-most argument "$1" will be replaced by "$2", so really you only need to access "$1".
say-goodbye.bash
#!/bin/bash while [ $# -gt 0 ] do echo "Goodbye ${1}" shift done
- $# variable holding the argument count shrinks each shift until it is 0
- '-gt 0' (greater than zero) test fails after all arguments have been shifted off
- "${1}" is the same as "$1" and is more readable in quoted text
command line
chmod +x say-goodbye.bash ./say-goodbye.bash "Taco Bell Enchirito" "Wendy's Chili, Chips & Cheese"
output
Goodbye Taco Bell Enchirito Goodbye Wendy's Chili, Chips & Cheese
Additional Notes
for ARG; do echo "$ARG"; done
If you are trying to make sense of what is happening here, that is where things get tricky. Nowhere are you specifying that you are actually enumerating arguments, the word ARG could have been named anything (as demonstrated below). It appears that a for statement that doesn't specify what is being iterated will have assumed it was arguments that was wanted. After iterating the arguments they can be re-iterated again (also shown below).
print-args-twice.bash
#!/bin/bash for ARG; do echo "$ARG" done for blah; do echo "$blah" done
command line
chmod +x print-args-twice.bash ./print-args-twice.bash a b
output
a b a b
Alternative test command syntax
The while condition can also be written with the test command syntax which is equivalent to the square bracket notation.
#while [ $# -gt 0 ] while test $# -gt 0 do echo "Goodbye ${1}" shift done