Prefer mapfile
or read -a
to split command output (or quote to avoid splitting).
Problematic code:
array=( $(mycommand) )
Correct code:
If it outputs multiple lines, each of which should be an element:
# For bash 4.4+, must not be in posix mode, may use temporary files
mapfile -t array < <(mycommand)
# For bash 3.x+, must not be in posix mode, may use temporary files
array=()
while IFS='' read -r line; do array+=("$line"); done < <(mycommand)
# For ksh, and bash 4.2+ with the lastpipe option enabled (may require disabling monitor mode)
array=()
mycommand | while IFS="" read -r line; do array+=("$line"); done
If it outputs a line with multiple words (separated by spaces, other delimiters can be chosen with IFS), each of which should be an element:
# For bash, uses temporary files
IFS=" " read -r -a array <<< "$(mycommand)"
# For bash 4.2+ with the lastpipe option enabled (may require disabling monitor mode)
array=()
mycommand | IFS=" " read -r -a array
# For ksh
IFS=" " read -r -A array <<< "$(mycommand)"
If the output should be a single element:
array=( "$(mycommand)" )
Rationale:
You are doing unquoted command expansion in an array. This will invoke the shell's sloppy word splitting and glob expansion.
Instead, prefer explicitly splitting (or not splitting):
- If you want to split the output into lines or words, use
mapfile
,read -ra
and/orwhile
loops as appropriate. - If the command output should become a single array element, quote it.
This prevents the shell from doing unwanted splitting and glob expansion, and therefore avoiding problems with output containing spaces or special characters.
Exceptions:
If you have already taken care (through setting IFS and set -f
) to have word splitting work the way you intend, you can ignore this warning.
Another exception is the wish for error handling: array=( $(mycommand) ) || die-with-error
works the way it looks while a similar mapfile
construct like mapfile -t array < <(mycommand)
doesn't fail and you will have to write more code for error handling.