Bash sh 特殊参数 $@ 和 "$@",如何将 "$@" 保存到一个变量中?

4

当我在编写shell脚本时,$@和"$@"问题让我有些沮丧。因此,我编写了一个shell脚本进行一些测试,如下所示。

func()
{
    local a="$@"
    for i in "$a";  do
        echo "$i ****"
    done
}

func000()
{
    local a="$@"
    for i in $a;  do
        echo "$i ****"
    done
}

func0()
{
    local a=$@
    for i in "$a";  do
            echo "$i ****"
    done
}

func00()
{
    local a=$@
    for i in $a;  do
        echo "$i ****"
    done
}

func1()
{
    for i in "$@";  do
        echo "$i ****"
    done
}

func2() 
{
    for i in $@;  do
        echo "$i ****"
    done
}

func "a b c"
func a b c 

echo "-----------"
func0 "a b c"
func0 a b c 

echo "-----------"
func00 "a b c"
func00 a b c 

echo "-----------"
func000 "a b c"
func000 a b c 

echo "-----------"
func1 "a b c"
func1 a b c 

echo "-----------"
func2 "a b c"
func2 a b c 




a b c ****
a b c ****   func
-----------
a b c ****   func0
a b c ****
-----------
a ****       func00
b ****
c ****
a ****
b ****
c ****
------------
a ****   func000
b ****
c ****
a ****
b ****
c ****

-----------
a b c ****     func1   //this has the result that I want. 
a **** 
b ****
c ****
----------- 
a ****      func2
b ****
c ****
a ****
b ****
c ****

据我所记,在使用 $@ 时,我们必须使用双引号,否则会出现一些问题。因此,我知道某些函数肯定不能正常工作。(无论如何,我仍然测试它)。
只有 func1 给我想要的结果,但问题是我想将 "$@" 分配给一个变量。通过查看 func0、func000、func0、func00 的结果,没有一个能给我正确的东西。因此,我希望有人能帮帮我。
此外,我知道 sh 和 bash 有区别。如果有人能指出在哪种情况下会出现问题,我将非常高兴。谢谢。
更新:
我应该说,这个脚本的结果取决于 bash 或 sh 的版本,或者 freebsd sh 与 linux sh 的版本。
我可能是错的,如果是这样,请指出来,非常感谢。
在旧版 sh、freebsd sh 中,我们没有数组,显然,数组赋值不能正常工作,替代方案是像我在 func000 中所做的那样,用字符串来定义变量,例如 local a="$a"; for i in $a; do ...。
如果使用 linux sh 4.5(已测试)或 bash,请参见我的答案。
3个回答

6

更新,感谢@jordanm的指导,我意识到这是bash和旧版本sh之间的区别。

这是在bash或sh 4.2中的解决方案。在旧版本sh中可能无法正常工作。

  33 func3()                                                                         
  34 {                                                                               
  35     local a=("${@}")                                                            
  36                                                                                 
  37     for i in "${a[@]}"; do                                                      
  38         echo "$i ****"                                                          
  39     done                                                                        
  40 }        

$@不是一个变量,它是一个变量数组。 - anishsane
1
$@ 不是一个数组(例如 ${@[1]} 是非法的)。然而,当被引用时,它的行为是特殊的,类似于数组:"$@" 等同于 "$1" "$2" "$3" ...(与 "$*" 相反,后者基本上等同于 "$1 $2 $3 $4")。 - chepner
如果您想明确声明它为数组,请使用以下代码:local -a a=( "$@" ) - Charles Duffy
顺便说一下,这应该在比4.2旧得多的bash中运行(但不会在任何基线/bin/sh中运行,因为数组是扩展...就像local关键字一样,尽管它很常见,我记得甚至ash也有它)。 - Charles Duffy

0

虽然我不是他,但我第一次听说以这种方式保存"${@}"是在偶然间发现Rich's sh tricks时。Rich在他的网站上提供了许多类似有用的、最重要的是可移植安全的方法来解决许多常见问题,但这里是一个摘录来回答你当前的问题:

处理数组

与 Bash 等“增强型” Bourne shell 不同,POSIX shell 没有数组类型。但是,通过使用纯 POSIX sh,您可以在紧急情况下获得类似于数组的语义,尽管效率会有些低下。诀窍在于您确实有一个(且仅有一个)数组——位置参数“$1”“$2”等——并且您可以将东西从该数组中替换出来。

替换“$@”数组的内容很容易:

set -- foo bar baz boo

或者,更有用的是:

set -- *

不清楚的是如何保存当前 "$@" 的内容,以便在替换后重新获取它,并且如何以编程方式生成这些“数组”。尝试使用先前引用技巧基于此函数:

save () {
 for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}

使用方法类似于:

myarray=$(save "$@")
set -- foo bar baz boo
eval "set -- $myarray"

-2

如果你想保留所有的引号,你可以尝试这个:

test() {
    local args="$@"
    for i in `echo "$args"`;  do
        echo "$i ****"
    done
}

test 1 2 3 4

输出:

~ $ ./test.sh 
1 ****
2 ****
3 ****
4 ****

你的测试并不是真正有效的,因为他所有的示例都有包含空格的参数,而你的示例也会在这种情况下出错。 - jordanm
说得好。我没有完全理解这个问题。我的测试将带有空格的单引号参数视为多个参数相同。 - theon

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