zsh vs bash:括号如何改变变量赋值行为?
zsh vs bash: how do parenthesis alter variable assignment behavior?
我对如何在不同的现有 shell 中处理变量赋值和括号有一些麻烦和误解。
目前令我困惑的是:
始终使用以下命令
./script.sh a b c d
当运行以下代码
#!/bin/zsh
bar=$@
for foo in $bar
do
echo $foo
done
输出是
a b c d
并与
#!/bin/zsh
bar=($@)
for foo in $bar
do
echo $foo
done
是(我最初想要的)
a
b
c
d
但使用 bash 或 sh
#!/bin/bash
bar=$@
for foo in $bar
do
echo $foo
done
给予
a
b
c
d
和
#!/bin/bash
bar=($@)
for foo in $bar
do
echo $foo
done
只是
a
那里发生了什么事?
当你这样做时:
bar=($@)
您实际上是在创建一个bashshell数组。要迭代 bash 数组,请使用:
bar=( "$@" ) # safer way to create array
for foo in "${bar[@]}"
do
echo "$foo"
done
联合行动
对于所涉及的两个 shell,给出的示例将假设一个显式设置的 argv 列表:
# this sets to "first entry", to "second entry", etc
$ set -- "first entry" "second entry" "third entry"
在两个 shell 中,declare -p
可用于以明确的形式发出变量名称的值,尽管它们表示该形式的方式可能有所不同。
在bash
bash 中的扩展规则通常与 ksh 兼容,并且在适用的情况下与 POSIX sh 语义兼容。与这些 shell 兼容需要不带引号的扩展执行字符串拆分和 glob 扩展(例如,将 *
替换为当前目录中的文件列表)。
在变量赋值中使用括号使其成为数组。比较这三个作业:
# this sets arr_str="first entry second entry third entry"
$ arr_str=$@
$ declare -p arr_str
declare -- arr="first entry second entry third entry"
# this sets arr=( first entry second entry third entry )
$ arr=( $@ )
declare -a arr='([0]="first" [1]="entry" [2]="second" [3]="entry" [4]="third" [5]="entry")'
# this sets arr=( "first entry" "second entry" "third entry" )
$ arr=( "$@" )
$ declare -p arr
declare -a arr='([0]="first entry" [1]="second entry" [2]="third entry")'
同样,在展开时,引号和印记很重要:
# quoted expansion, first item only
$ printf '%s\n' "$arr"
first entry
# unquoted expansion, first item only: that item is string-split into two separate args
$ printf '%s\n' $arr
first
entry
# unquoted expansion, all items: each word expanded into its own argument
$ printf '%s\n' ${arr[@]}
first
entry
second
entry
third
entry
# quoted expansion, all items: original arguments all preserved
$ printf '%s\n' "${arr[@]}"
first entry
second entry
third entry
在 zsh 中
zsh 做了很多神奇的事情来尝试按照用户的意思去做,而不是与历史 shell(ksh、POSIX sh 等)兼容。然而,即使在那里,做错事也会有你想要的结果之外的结果:
# Assigning an array to a string still flattens it in zsh
$ arr_str=$@
$ declare -p arr_str
typeset arr_str='first entry second entry third entry'
# ...but quotes aren't needed to keep arguments together on array assignments.
$ arr=( $@ )
$ declare -p arr
typeset -a arr
arr=('first entry' 'second entry' 'third entry')
# in zsh, expanding an array always expands to all entries
$ printf '%s\n' $arr
first entry
second entry
third entry
# ...and unquoted string expansion doesn't do string-splitting by default:
$ printf '%s\n' $arr_str
first entry second entry third entry
我对如何在不同的现有 shell 中处理变量赋值和括号有一些麻烦和误解。
目前令我困惑的是:
始终使用以下命令
./script.sh a b c d
当运行以下代码
#!/bin/zsh
bar=$@
for foo in $bar
do
echo $foo
done
输出是
a b c d
并与
#!/bin/zsh
bar=($@)
for foo in $bar
do
echo $foo
done
是(我最初想要的)
a
b
c
d
但使用 bash 或 sh
#!/bin/bash
bar=$@
for foo in $bar
do
echo $foo
done
给予
a
b
c
d
和
#!/bin/bash
bar=($@)
for foo in $bar
do
echo $foo
done
只是
a
那里发生了什么事?
当你这样做时:
bar=($@)
您实际上是在创建一个bashshell数组。要迭代 bash 数组,请使用:
bar=( "$@" ) # safer way to create array
for foo in "${bar[@]}"
do
echo "$foo"
done
联合行动
对于所涉及的两个 shell,给出的示例将假设一个显式设置的 argv 列表:
# this sets to "first entry", to "second entry", etc
$ set -- "first entry" "second entry" "third entry"
在两个 shell 中,declare -p
可用于以明确的形式发出变量名称的值,尽管它们表示该形式的方式可能有所不同。
在bash
bash 中的扩展规则通常与 ksh 兼容,并且在适用的情况下与 POSIX sh 语义兼容。与这些 shell 兼容需要不带引号的扩展执行字符串拆分和 glob 扩展(例如,将 *
替换为当前目录中的文件列表)。
在变量赋值中使用括号使其成为数组。比较这三个作业:
# this sets arr_str="first entry second entry third entry"
$ arr_str=$@
$ declare -p arr_str
declare -- arr="first entry second entry third entry"
# this sets arr=( first entry second entry third entry )
$ arr=( $@ )
declare -a arr='([0]="first" [1]="entry" [2]="second" [3]="entry" [4]="third" [5]="entry")'
# this sets arr=( "first entry" "second entry" "third entry" )
$ arr=( "$@" )
$ declare -p arr
declare -a arr='([0]="first entry" [1]="second entry" [2]="third entry")'
同样,在展开时,引号和印记很重要:
# quoted expansion, first item only
$ printf '%s\n' "$arr"
first entry
# unquoted expansion, first item only: that item is string-split into two separate args
$ printf '%s\n' $arr
first
entry
# unquoted expansion, all items: each word expanded into its own argument
$ printf '%s\n' ${arr[@]}
first
entry
second
entry
third
entry
# quoted expansion, all items: original arguments all preserved
$ printf '%s\n' "${arr[@]}"
first entry
second entry
third entry
在 zsh 中
zsh 做了很多神奇的事情来尝试按照用户的意思去做,而不是与历史 shell(ksh、POSIX sh 等)兼容。然而,即使在那里,做错事也会有你想要的结果之外的结果:
# Assigning an array to a string still flattens it in zsh
$ arr_str=$@
$ declare -p arr_str
typeset arr_str='first entry second entry third entry'
# ...but quotes aren't needed to keep arguments together on array assignments.
$ arr=( $@ )
$ declare -p arr
typeset -a arr
arr=('first entry' 'second entry' 'third entry')
# in zsh, expanding an array always expands to all entries
$ printf '%s\n' $arr
first entry
second entry
third entry
# ...and unquoted string expansion doesn't do string-splitting by default:
$ printf '%s\n' $arr_str
first entry second entry third entry