rah*_*hmu 119 shell ksh quoting arguments
考虑以下代码:
foo () {
echo $*
}
bar () {
echo $@
}
foo 1 2 3 4
bar 1 2 3 4
Run Code Online (Sandbox Code Playgroud)
它输出:
1 2 3 4
1 2 3 4
我正在使用 Ksh88,但我也对其他常见的 shell 感兴趣。如果您碰巧知道特定 shell 的任何特殊性,请务必提及它们。
我在 Solaris 上的 Ksh 手册页中找到了以下内容:
$* 和 $@ 的含义在不引用或用作参数赋值或文件名时是相同的。然而,当用作命令参数时,$* 等价于 ``$1d$2d...'',其中 d 是 IFS 变量的第一个字符,而 $@ 等价于 $1 $2 ....
我尝试修改IFS变量,但它不会修改输出。也许我做错了什么?
Mik*_*kel 140
当它们没有被引用时,$*并且$@是相同的。您不应使用其中任何一个,因为一旦您的参数包含空格或通配符,它们就会意外中断。
"$*"扩展为一个词"$1c$2c..."。通常c是一个空格,但它实际上是 的第一个字符IFS,因此它可以是您选择的任何内容。
我发现它的唯一好的用途是:
用逗号连接参数(简单版本)
join1() {
typeset IFS=,
echo "$*"
}
join1 a b c # => a,b,c
Run Code Online (Sandbox Code Playgroud)
使用指定的分隔符连接参数(更好的版本)
join2() {
typeset IFS=$1 # typeset makes a local variable in ksh (see footnote)
shift
echo "$*"
}
join2 + a b c # => a+b+c
Run Code Online (Sandbox Code Playgroud)
"$@" 扩展为单独的单词: "$1" "$2" ...
这几乎总是你想要的。它将每个位置参数扩展为一个单独的词,这使得它非常适合接收命令行或函数参数,然后将它们传递给另一个命令或函数。并且因为它使用双引号进行扩展,这意味着如果"$1"包含空格或星号 ( *) ,则内容不会中断。
让我们编写一个名为的脚本svim,该脚本vim使用sudo. 我们将做三个版本来说明差异。
svim1
#!/bin/sh
sudo vim $*
Run Code Online (Sandbox Code Playgroud)
svim2
#!/bin/sh
sudo vim "$*"
Run Code Online (Sandbox Code Playgroud)
svim3
#!/bin/sh
sudo vim "$@"
Run Code Online (Sandbox Code Playgroud)
所有这些都适用于简单的情况,例如不包含空格的单个文件名:
svim1 foo.txt # == sudo vim foo.txt
svim2 foo.txt # == sudo vim "foo.txt"
svim2 foo.txt # == sudo vim "foo.txt"
Run Code Online (Sandbox Code Playgroud)
但是,只有$*和"$@"工作,如果正常,你有多个参数。
svim1 foo.txt bar.txt # == sudo vim foo.txt bar.txt
svim2 foo.txt bar.txt # == sudo vim "foo.txt bar.txt" # one file name!
svim3 foo.txt bar.txt # == sudo vim "foo.txt" "bar.txt"
Run Code Online (Sandbox Code Playgroud)
而只有"$*"和"$@"正常工作,如果你有一个包含空格的参数。
svim1 "shopping list.txt" # == sudo vim shopping list.txt # two file names!
svim2 "shopping list.txt" # == sudo vim "shopping list.txt"
svim3 "shopping list.txt" # == sudo vim "shopping list.txt"
Run Code Online (Sandbox Code Playgroud)
所以只会一直"$@"正常工作。
typeset是如何在ksh(bash并ash使用local) 中创建局部变量。这意味着IFS当函数返回时将恢复到其先前的值。这很重要,因为如果IFS设置为非标准的东西,您之后运行的命令可能无法正常工作。
Gil*_*il' 41
简短回答:使用"$@"(注意双引号)。其他形式很少有用。
"$@"是一种相当奇怪的语法。它被所有位置参数替换,作为单独的字段。如果没有位置参数($#为 0),则"$@"展开为空(不是空字符串,而是一个包含 0 个元素的列表),如果有一个位置参数则"$@"相当于"$1",如果有两个位置参数则"$@"相当于"$1" "$2", 等等。
"$@"允许您将脚本或函数的参数传递给另一个命令。这对于在调用具有与调用包装器相同的参数和选项的命令之前执行设置环境变量、准备数据文件等操作的包装器非常有用。
例如,以下函数过滤 的输出cvs -nq update。除了输出过滤和返回状态(这是 的状态grep而不是 的状态cvs),调用cvssm某些参数的行为就像cvs -nq update使用这些参数调用一样。
cvssm () { cvs -nq update "$@" | egrep -v '^[?A]'; }
Run Code Online (Sandbox Code Playgroud)
"$@"扩展到位置参数列表。在支持数组的 shell 中,有一个类似的语法可以扩展到数组的元素列表:("${array[@]}"除了在 zsh 中,大括号是必需的)。同样,双引号有点误导:它们防止数组元素的字段拆分和模式生成,但每个数组元素最终都在自己的字段中。
一些古老的 shell 有一个可以说是一个错误:当没有位置参数时,"$@"扩展为包含空字符串的单个字段,而不是没有字段。这导致了解决方法${1+"$@"}(通过 Perl 文档而闻名)。只有实际 Bourne shell 和 OSF1 实现的旧版本会受到影响,其现代兼容替代品(ash、ksh、bash 等)都不会受到影响。/bin/sh在我所知道的 21 世纪发布的任何系统上都不会受到影响(除非你算上 Tru64 维护版本,甚至有/usr/xpg4/bin/sh安全版本,所以只有#!/bin/sh脚本会受到影响,#!/usr/bin/env sh只要你的 PATH 设置为符合 POSIX 标准,脚本不会受到影响) . 总之,这是一个你不必担心的历史轶事。
"$*"总是扩展为一个词。这个词包含位置参数,中间用空格连接。(更一般地,分隔符是IFS变量值的第一个字符。如果 的值为IFS空字符串,则分隔符为空字符串。)如果没有位置参数"$*",则为空字符串,如果有两个位置参数并IFS具有其默认值,则"$*"等效于"$1 $2"等。
$@和$*外部引号是等效的。它们扩展到位置参数列表,作为单独的字段,例如"$@"; 但是每个结果字段然后被拆分为单独的字段,这些字段被视为文件名通配符模式,就像通常使用不带引号的变量扩展一样。
例如,如果当前目录包含三个文件bar,baz和foo,则:
set -- # no positional parameters
for x in "$@"; do echo "$x"; done # prints nothing
for x in "$*"; do echo "$x"; done # prints 1 empty line
for x in $*; do echo "$x"; done # prints nothing
set -- "b* c*" "qux"
echo "$@" # prints `b* c* qux`
echo "$*" # prints `b* c* qux`
echo $* # prints `bar baz c* qux`
for x in "$@"; do echo "$x"; done # prints 2 lines: `b* c*` and `qux`
for x in "$*"; do echo "$x"; done # prints 1 lines: `b* c* qux`
for x in $*; do echo "$x"; done # prints 4 lines: `bar`, `baz`, `c*` and `qux`
Run Code Online (Sandbox Code Playgroud)
cuo*_*glm 30
下面是一个简单的脚本来演示的区别$*和$@:
#!/bin/bash
test_param() {
echo "Receive $# parameters"
echo Using '$*'
echo
for param in $*; do
printf '==>%s<==\n' "$param"
done;
echo
echo Using '"$*"'
for param in "$*"; do
printf '==>%s<==\n' "$param"
done;
echo
echo Using '$@'
for param in $@; do
printf '==>%s<==\n' "$param"
done;
echo
echo Using '"$@"';
for param in "$@"; do
printf '==>%s<==\n' "$param"
done
}
IFS="^${IFS}"
test_param 1 2 3 "a b c"
Run Code Online (Sandbox Code Playgroud)
输出:
% cuonglm at ~
% bash test.sh
Receive 4 parameters
Using $*
==>1<==
==>2<==
==>3<==
==>a<==
==>b<==
==>c<==
Using "$*"
==>1^2^3^a b c<==
Using $@
==>1<==
==>2<==
==>3<==
==>a<==
==>b<==
==>c<==
Using "$@"
==>1<==
==>2<==
==>3<==
==>a b c<==
Run Code Online (Sandbox Code Playgroud)
在数组语法中,使用$*or时没有区别$@。只有当您将它们与双引号"$*"和"$@".
Woj*_*tek 11
您提供的代码将给出相同的结果。为了更好地理解它,试试这个:
foo () {
for i in "$*"; do
echo "$i"
done
}
bar () {
for i in "$@"; do
echo "$i"
done
}
Run Code Online (Sandbox Code Playgroud)
输出现在应该不同了。这是我得到的:
$ foo() 1 2 3 4
1 2 3 4
$ bar() 1 2 3 4
1
2
3
4
Run Code Online (Sandbox Code Playgroud)
这对我有用bash。据我所知,ksh 应该差别不大。本质上,引用$*会将所有内容视为一个词,而引用$@会将列表视为单独的词,如上例所示。
作为使用IFS变量 with的示例$*,请考虑以下内容
fooifs () {
IFS="c"
for i in "$*"; do
echo "$i"
done
unset IFS # reset to the original value
}
Run Code Online (Sandbox Code Playgroud)
我得到这个结果:
$ fooifs 1 2 3 4
1c2c3c4
Run Code Online (Sandbox Code Playgroud)
另外,我刚刚确认它在ksh. 双方bash并ksh在这里测试了OSX下,但我看不出怎么会多大关系。
在编写应该以正确方式使用位置参数的脚本时,差异很重要......
想象一下下面的调用:
$ myuseradd -m -c "Carlos Campderrós" ccampderros
Run Code Online (Sandbox Code Playgroud)
这里只有4个参数:
$1 => -m
$2 => -c
$3 => Carlos Campderrós
$4 => ccampderros
Run Code Online (Sandbox Code Playgroud)
就我而言,myuseradd它只是一个useradd接受相同参数的包装器,但为用户添加了配额:
#!/bin/bash -e
useradd "$@"
setquota -u "${!#}" 10000 11000 1000 1100
Run Code Online (Sandbox Code Playgroud)
注意对 的调用useradd "$@",带$@引号。这将尊重参数并将它们按原样发送到useradd. 如果您要取消引用$@(或也使用$*未引用),useradd 将看到5 个参数,因为包含空格的第三个参数将被拆分为两个:
$1 => -m
$2 => -c
$3 => Carlos
$4 => Campderrós
$5 => ccampderros
Run Code Online (Sandbox Code Playgroud)
(反之,如果你使用"$*",useradd的只会看到一个参数:-m -c Carlos Campderrós ccampderros)
因此,简而言之,如果您需要处理尊重多字参数的参数,请使用"$@".
* Expands to the positional parameters, starting from one. When
the expansion occurs within double quotes, it expands to a sin?
gle word with the value of each parameter separated by the first
character of the IFS special variable. That is, "$*" is equiva?
lent to "$1c$2c...", where c is the first character of the value
of the IFS variable. If IFS is unset, the parameters are sepa?
rated by spaces. If IFS is null, the parameters are joined
without intervening separators.
@ Expands to the positional parameters, starting from one. When
the expansion occurs within double quotes, each parameter
expands to a separate word. That is, "$@" is equivalent to "$1"
"$2" ... If the double-quoted expansion occurs within a word,
the expansion of the first parameter is joined with the begin?
ning part of the original word, and the expansion of the last
parameter is joined with the last part of the original word.
When there are no positional parameters, "$@" and $@ expand to
nothing (i.e., they are removed).
Run Code Online (Sandbox Code Playgroud)
//人 bash。是 ksh,公平,类似的行为。