Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does declare reset the IFS in bash?

Tags:

bash

declare

ifs

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.

like image 353
Camden Narzt Avatar asked Dec 21 '25 13:12

Camden Narzt


1 Answers

The thing that surprises you here is that declare is a command, and follows the same syntax rules as other commands.

  • In 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.
  • In 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()' -
)
like image 65
Charles Duffy Avatar answered Dec 24 '25 04:12

Charles Duffy



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!