Bash for loop with spaces Bash for loop with spaces shell shell

Bash for loop with spaces


COMMANDS=("ls /" "df ~" "du -hs ~/Devel/")for i in "${COMMANDS[@]}"; do   echo "$i"done

This uses an array to store the commands. This feature is also available in ksh, zsh, but not in sh.

Arrays behave like the "$@" argument array. Applying a for loop on "${ARRAY_NAME[@]}" (the quotes are important) will give you each item in succession. If you omit the quotes, it'll all get smushed together and split on the separators present in your IFS environment variable ('\t', '\n' and ' ' by default).


I'd recommend you not do that at all. First, it's much longer and more complicated than simply writing

ls /df ~du -hs ~/Devel/

Second, flat strings are not able to store nested, space-delimited strings. There is no way (and yes, I'm ignoring eval) to differentiate between spaces that separate commands and spaces that separate arguments within a command. You can use an array for simple commands, but you can't nest arrays, so as soon as the arguments for one of your commands contain spaces, you are back to the original problem.

commands=("ls /" "df ~" "du -hs ~/Devel")  # OK, but...commands=("ls \"foo bar\"" "echo 'hello world'")  # No.

If you want your script to be able to run arbitrary commands specified by a user, have it source files from a known directory instead (that is, implement a plug-in system).

command_dir=~/myscript_pluginsfor f in "$command_dir"; do    source "$f"done

where $command_dir contains one file for each of the commands you want to run.

Or, define a series of functions, and store their names in an string (function names can't contain spaces, so there's no need for arrays):

lister () { ls /; }dfer () { df ~; }duer () { du -hs ~/Devel; }commands="lister dfer duer"for command in $commands; do     $commanddone

or

commands=(lister dfer duer)for command in "${commands[@]}"; do    $commanddone

Further reading: I'm trying to put a command in a variable, but the complex cases always fail!