Why does this work to create an array out of the newline separated elements:
declare -a themes
IFS=$'\n' themes=($(plutil -extract 'Window Settings' xml1 -o - - <<< "$terminal_settings" | xmllint --xpath '/plist/dict/key/node()' -))
but this doesn't, instead making an array of space separated elements:
IFS=$'\n' declare -a themes=($(plutil -extract 'Window Settings' xml1 -o - - <<< "$terminal_settings" | xmllint --xpath '/plist/dict/key/node()' -))
Normally when IFS won't work for a specific line, it must be set before the line, then reset afterwards, but in this case setting it for the one line is fine, but if the line contains declare it doesn't work.
The thing that surprises you here is that declare
is a command, and follows the same syntax rules as other commands.
var=value somecommand args...
, var=value
is exported (for a regular command, or set in the current environment for special built-in commands) during the execution of somecommand
, and unset afterwards.var1=value1 var2=value2 var3=value3
, nothing is transient, because it's just a sequence of assignments; all these assignments, including var1=value1
, persist.This is also true of local
, typeset
, etc.
That said, insofar as your goal is to read lines into an array, array=( $(...) )
is the wrong tool for the job anyhow -- string splitting also invokes globbing behavior, so if you had a line containing only *
it would be replaced with a list of filenames; shell flags like nullglob
and globfail
apply; etc.
Supporting bash 3.2+, and also ensuring that read
fails if the plutil | xmllint
pipeline itself returns a nonzero exit status (you might want to use set -o pipefail
to make that happen if plutil
fails even should xmllint
succeed):
IFS=$'\n' read -r -d '' -a themes < <(
plutil -extract 'Window Settings' xml1 -o - - <<< "$terminal_settings" \
| xmllint --xpath '/plist/dict/key/node()' - \
&& printf '\0'
)
Terser, but limited to bash 4.0+, and not passing failures back to the parent:
readarray -t themes <(
plutil -extract 'Window Settings' xml1 -o - - <<< "$terminal_settings" \
| xmllint --xpath '/plist/dict/key/node()' -
)