zsh与bash:如何在变量赋值行为中使用括号?

3

我对不同的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

it is (what I initially wanted)

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

那里发生了什么?
2个回答

3
当您执行以下操作时:
bar=($@)

实际上,你正在创建一个bash shell数组。要遍历bash数组,请使用:

bar=( "$@" ) # safer way to create array
for foo in "${bar[@]}"
do
    echo "$foo"
done

1
创建一个数组时要小心,需要使用bar=( "$@" )来防止字符串分割和全局匹配。 - Charles Duffy
感谢@CharlesDuffy,我已相应地编辑了答案。 - anubhava

3

联合操作

对于涉及的两个shell,所给出的示例将假定已明确设置了argv列表:

# this sets $1 to "first entry", $2 to "second entry", etc
$ set -- "first entry" "second entry" "third entry"

在两个Shell中,declare -p 可以用于以明确的形式输出变量名的值,但它们表示该形式的方式可能会有所不同。

在bash中

bash中的扩展规则通常与ksh和POSIX sh语义兼容。与这些Shell兼容需要未加引号的扩展执行字符串拆分和全局匹配(例如将*替换为当前目录中的文件列表)。
在变量赋值中使用圆括号会使它成为一个数组。比较下面这三个赋值的区别:
# 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")'

同样,在展开时,引号和Sigil很重要:

# 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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接