Size: 2729
Comment: Put double quotes around the expansion of "${arr[*]}". (Not needed for the particular value assigned to arr, but in general is needed to preserve white space in the array elements.)
|
← Revision 11 as of 2022-08-01 14:45:16 ⇥
Size: 3284
Comment: unset => unset -v
|
Deletions are marked like this. | Additions are marked like this. |
Line 10: | Line 10: |
$ unset foo | $ unset -v foo |
Line 23: | Line 23: |
* The `echo` command is run, with an empty string as an argument, and `foo=bar` in its environment. But since `echo` doesn't care about environment variables, it ignores that. | * The `echo` command is run, with an empty string as an argument, and `foo=bar` in its environment. But since `echo` doesn't care about environment variables, it ignores that, and prints only a newline. |
Line 37: | Line 37: |
* The child Bash process expands the `$foo` using the value from the environment and hands that value to `echo`. | * The child Bash process expands `$foo` using the value from the environment and hands that value to `echo`. * `echo` receives `bar` as its only argument, and prints it, plus a newline. |
Line 41: | Line 42: |
----- * '''In The Manual — [[https://www.gnu.org/software/bash/manual/html_node/Simple-Command-Expansion.html#Simple-Command-Expansion|Simple Command Expansion]]''' ----- |
|
Line 43: | Line 48: |
arr=('Var 1' 'Var 2' 'Var 3' 'Var 4') | arr=('Var 1' 'Var 2' 'Var 3' 'Var 4') |
Line 45: | Line 50: |
# join each array element with a ";" # Traditional solution: set IFS, then unset it afterward IFS=\; joinedVariable="${arr[*]}" unset -v IFS |
# join each array element with a ";" # Traditional solution: set IFS, then unset it afterward IFS=\; joinedVariable="${arr[*]}" unset -v IFS |
Line 51: | Line 56: |
# Alternative solution: temporarily set IFS for the duration of eval IFS=\; command eval 'JoinedVariable="${arr[*]}"' |
# Alternative one: temporarily set IFS for the duration of eval. # Double-quotes work around a Bash bug in versions < 4.3.0 IFS=\; command eval 'JoinedVariable="${arr[*]}"' # Alternative two: a function. Local variables are not POSIX (as of 2017). # Usage: join joinchar [arg ...] join() { local IFS="$1" shift printf '%s\n' "$*" } |
Line 55: | Line 69: |
Here, the `eval` alternative is simpler and more elegant. Appropriate care must be taken [[BashFAQ/048|to ensure safety when using eval]]. The `command` prefix is required for all shells other than Bash plus Bash POSIX mode (see [[http://wiki.bash-hackers.org/commands/builtin/eval#using_the_environment]]). This won't work in recent versions of zsh due to an apparent regression (documented behavior broken for `setopt POSIX_BUILTINS`), or busybox due to a bug in that environment assignments fail to propagate in this case. (feel free to file bugs if anybody cares enough.) | Here, the `eval` alternative is simpler and more elegant than unsetting [[IFS]] (''In your opinion! -- GreyCat''). Appropriate care must be taken [[BashFAQ/048|to ensure safety when using eval]]. The `command` prefix is required for all shells other than Bash plus Bash POSIX mode (see [[http://wiki.bash-hackers.org/commands/builtin/eval#using_the_environment]]). This won't work in recent versions of zsh due to an apparent regression (documented behavior broken for `setopt POSIX_BUILTINS`), or busybox due to a bug in that environment assignments fail to propagate in this case. (feel free to file bugs if anybody cares enough.) |
Why doesn't foo=bar echo "$foo" print bar?
This is subtle, and has to do with the exact order in which the BashParser performs each step.
Many people, when they first learn about var=value command and how it temporarily sets a variable for the duration of a command, eventually work up an example like this one and become confused why it doesn't do what they expect.
As an illustration:
$ unset -v foo $ foo=bar echo "$foo" $ echo "$foo" $ foo=bar; echo "$foo" bar
The reason the first one prints a blank line is because of the order of these steps:
The parameter expansion of $foo is done first. An empty string is substituted for the quoted expression.
After that, Bash sets up a temporary environment and puts foo=bar in it.
The echo command is run, with an empty string as an argument, and foo=bar in its environment. But since echo doesn't care about environment variables, it ignores that, and prints only a newline.
This version works as we expect:
$ unset -v foo $ foo=bar bash -c 'echo "$foo"' bar
In this case, the following steps are performed:
A temporary environment is set up with foo=bar in it.
bash is invoked within that environment, and given -c and echo "$foo" as its two arguments.
The child Bash process expands $foo using the value from the environment and hands that value to echo.
echo receives bar as its only argument, and prints it, plus a newline.
It's not entirely clear, in all cases, why people ask us this question. Mostly they seem to be curious about the behavior, rather than trying to solve a specific problem; so I won't try to give any examples of "the right way to do things like this", since there's no real problem to solve.
* In The Manual — Simple Command Expansion
There are some special cases in Bash where understanding this can be useful. Take the following example:
arr=('Var 1' 'Var 2' 'Var 3' 'Var 4') # join each array element with a ";" # Traditional solution: set IFS, then unset it afterward IFS=\; joinedVariable="${arr[*]}" unset -v IFS # Alternative one: temporarily set IFS for the duration of eval. # Double-quotes work around a Bash bug in versions < 4.3.0 IFS=\; command eval 'JoinedVariable="${arr[*]}"' # Alternative two: a function. Local variables are not POSIX (as of 2017). # Usage: join joinchar [arg ...] join() { local IFS="$1" shift printf '%s\n' "$*" }
Here, the eval alternative is simpler and more elegant than unsetting IFS (In your opinion! -- GreyCat). Appropriate care must be taken to ensure safety when using eval. The command prefix is required for all shells other than Bash plus Bash POSIX mode (see http://wiki.bash-hackers.org/commands/builtin/eval#using_the_environment). This won't work in recent versions of zsh due to an apparent regression (documented behavior broken for setopt POSIX_BUILTINS), or busybox due to a bug in that environment assignments fail to propagate in this case. (feel free to file bugs if anybody cares enough.)