4

As I understand and read it, $@ turns into "arg1" "arg2" "arg3".
But it seems to have some weird behavior in some cases when quoted.
 

Test 1, $@

$ function somefunc { sh -c "echo $@"; }
$ somefunc 1 2 3
> + somefunc 1 2 3
> + sh -c 'echo 1' 2 3
> 1

The expected output is 1 2 3, the output I get is 1. Now I see that, for some reason, it seems to only pass echo 1 as command, but why does "echo "1" "2" "3"" turn into it? I would maybe have expected echo  because the " before echo may get closed by the " before 1.
 
Test 2, $*

$ function somefunc { sh -c "echo $*"; }
$ somefunc 1 2 3
> + somefunc 1 2 3
> + sh -c 'echo 1 2 3'
> 1 2 3

This is pretty obvious and works, just to make sure. After all $* passes 1 2 3, not "1" "2" "3".
 
Test 3, $@ and $*

$ function somefunc { sh -c "echo $@ $*"; }
$ somefunc 1 2 3
> + somefunc 1 2 3
> + sh -c 'echo 1 2 3 1 2 3'
> 1 2 3 1 2 3

This seems to work again weirdly enough. But I don't get how "echo "1" "2" "3" 1 2 3" turns into 'echo 1 2 3 1 2 3' and doesn't follow the "pattern" that only "echo $@"did.
 

Test 4, $@ and a string

$ function somefunc { sh -c "echo $@ hi"; }
$ somefunc 1 2 3
> + somefunc 1 2 3
> + sh -c 'echo 1' 2 '3 hi'
> 1

This again follows the pattern of "echo $@", even thought, as I see it, it is pretty much the same as "echo $@ $*" since $* turns into a string after all.
What also puzzles me is that it turned into 'echo 1' 2 '3 hi', the first test would have suggested 'echo 1' 2 3 hi (without the two ' surrounding 3 hi)

Test 5, $@ and $var

$ var="hi"
> + var=hi
$ function somefunc { sh -c "echo $@ $var"; }
$ somefunc 1 2 3
> + somefunc 1 2 3
> + sh -c 'echo 1 2 3 hi'
> 1 2 3 hi

This works again. So $@ seems to work if another variable follows it.

The tests are also possible with su user -c or bash -c instead of sh -c, so I suppose also every other command that executes the next given argument.

I now seem to have gotten some kind of behavior out of it,
but I do still not understand this behavior.
What am I missing here?

4

3 回答 3

2

sh 语言规范有这样的说法$@

扩展到位置参数,从一个开始。当扩展发生在双引号内时,并且在执行字段拆分(请参阅字段拆分)时,每个位置参数都应扩展为一个单独的字段,前提是第一个参数的扩展仍应与开头部分连接原词(假设扩展参数嵌入在一个词中),最后一个参数的扩展仍应与原词的最后部分连接。如果没有位置参数,“@”的扩展将生成零字段,即使“@”被双引号引起来。

这意味着 "echo $@ $*"位置参数所在的字符串1,应该扩展为2,以便您的测试 3 应该输出该字符串。bash 错误地将字符串扩展为并输出的事实表明 bash中存在错误。3"echo 1" "2" "3 1 2 3"1"echo 1 2 3 1 2 3"1 2 3 1 2 3

处理 sh 语法规则的这个(又一个)奇怪之处的常用技术是仅在 $@ 是一个不同的单词时在双引号中使用它。换句话说,可以写"$@",但不要放在$@双引号中,除非它是双引号内的唯一内容。

于 2012-06-30T16:45:55.680 回答
1

使用@BrianSwift 的提示,一切都非常有意义,除了Test3(在这里,我们得到三个参数的情况并非'echo '如此,第一个参数的前缀为,第三个参数的后缀为' $*'。相反,结果是单个参数) .

但这可能可以通过以下事实来解释:$@$*都是 shell 中存在特殊代码的“特殊”变量。我假设$*just 的存在覆盖了@BrianSwift 注意到的特殊行为,因为$*稍后会对其进行评估。

尝试交换顺序:somefunc() { sh -x "echo $* $@"; }。您将再次获得带有前缀和后缀的拆分行为,这似乎支持我的上述假设。

因此,总而言之,除了 Test3 的行为可能有点“未充分记录”之外,一切都可以通过参考手册来解释。

于 2012-06-30T12:25:59.680 回答
0

As I understand and read it, $@ turns into "arg1" "arg2" "arg3".

No, "$@" turns into "arg1" "arg2" "arg3". The quotes matter.

于 2012-06-30T00:54:13.447 回答