简体   繁体   中英

Bash for loop with spaces

I would like to do something like this:

COMMANDS='"ls /" "df ~" "du -hs ~/Devel/"'
for i in $COMMANDS; do
    echo $i
done

Where the result would be:

ls /
df ~
du -hs ~/Devel/

But I can't find the right syntax for the 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_plugins
for 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 
    $command
done

or

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM