$ mkdir "my dir" anotherdir
$ ls
anotherdir my dir
$ cp /dev/null "my dir/my file"
$ cp /dev/null "anotherdir/myfile"
$ ls -Fltr
total 0
drwxr-xr-x 3 jleffler staff 102 Nov 1 14:55 my dir/
drwxr-xr-x 3 jleffler staff 102 Nov 1 14:55 anotherdir/
$ ls -Fltr *
my dir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 my file
anotherdir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 myfile
$ ls -Fltr "./my dir" "./anotherdir"
./my dir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 my file
./anotherdir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 myfile
$ var='"./my dir" "./anotherdir"' && echo $var
"./my dir" "./anotherdir"
$ ls -Fltr $var
ls: "./anotherdir": No such file or directory
ls: "./my: No such file or directory
ls: dir": No such file or directory
$
为什么没有't that work? It doesn'工作,因为shell在扩展变量之前处理引号 . 所以,要让shell注意 $var 中嵌入的引号,你必须使用 eval :
$ eval ls -Fltr $var
./my dir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 my file
./anotherdir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 myfile
$
当你有文件名如“ He said, "Don't do this!" ”(带引号和双引号和空格)时,这会变得非常棘手 .
$ cp /dev/null "He said, \"Don't do this!\""
$ ls
He said, "Don't do this!" anotherdir my dir
$ ls -l
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 15:54 He said, "Don't do this!"
drwxr-xr-x 3 jleffler staff 102 Nov 1 14:55 anotherdir
drwxr-xr-x 3 jleffler staff 102 Nov 1 14:55 my dir
$
$ escape $var
'"./my' 'dir"' '"./anotherdir"'
$ escape "$var"
'"./my dir" "./anotherdir"'
$ escape x y z
x y z
$
我有另一个名为 al 的程序,它每行列出一个参数(它更古老:版本1 . 1日期为1987-01-27T14:35:49) . 它在调试脚本时最有用,因为它可以插入命令行以查看实际传递给命令的参数 .
$ echo "$var"
"./my dir" "./anotherdir"
$ al $var
"./my
dir"
"./anotherdir"
$ al "$var"
"./my dir" "./anotherdir"
$
[补充:现在为了显示各种 "$@" 符号之间的区别,这里还有一个例子:
$ cat xx.sh
set -x
al $@
al $*
al "$*"
al "$@"
$ sh xx.sh * */*
+ al He said, '"Don'\''t' do 'this!"' anotherdir my dir xx.sh anotherdir/myfile my dir/my file
He
said,
"Don't
do
this!"
anotherdir
my
dir
xx.sh
anotherdir/myfile
my
dir/my
file
+ al He said, '"Don'\''t' do 'this!"' anotherdir my dir xx.sh anotherdir/myfile my dir/my file
He
said,
"Don't
do
this!"
anotherdir
my
dir
xx.sh
anotherdir/myfile
my
dir/my
file
+ al 'He said, "Don'\''t do this!" anotherdir my dir xx.sh anotherdir/myfile my dir/my file'
He said, "Don't do this!" anotherdir my dir xx.sh anotherdir/myfile my dir/my file
+ al 'He said, "Don'\''t do this!"' anotherdir 'my dir' xx.sh anotherdir/myfile 'my dir/my file'
He said, "Don't do this!"
anotherdir
my dir
xx.sh
anotherdir/myfile
my dir/my file
$
请注意,没有任何内容保留命令行上 * 和 */* 之间的原始空白 . 另请注意,您可以使用以下命令更改shell中的'command line arguments':
set -- -new -opt and "arg with space"
这设置了4个选项,' -new ', ' -opt ', ' and ', and ' arg with space ' . ]
6 回答
对于简单的情况,您也可以使用
shift
. 它将参数列表视为一个队列,每个shift
抛出第一个参数,剩下的每个参数的数量递减 .使用
"$@"
表示所有参数:这将遍历每个参数并在单独的行上打印出来 . $ @的行为类似于$ *,但是当引用时,如果参数中有空格,则它们会被正确分解:
请注意,罗伯特的答案是正确的,它也适用于
sh
. 您可以(便携地)进一步简化它:相当于:
即,你什么都不需要!
测试(
$
是命令提示符):我首先在Kernighan和Pike的Unix编程环境中读到过这个 .
在
bash
,help for
文件中:您也可以将它们作为数组元素访问,例如,如果您不想遍历所有这些元素
重写VonC现在删除的答案 .
Robert Gamble的简洁回答直接涉及这个问题 . 这个放大了包含空格的文件名的一些问题 .
另见:${1:+"$@"} in /bin/sh
Basic thesis:
"$@"
是正确的,$*
(未引用)几乎总是错误的 . 这是因为"$@"
在参数包含空格时工作正常,而在$*
不工作时工作方式与$*
相同 . 在某些情况下,"$*"
也可以,但"$@"
通常(但不总是)在同一个地方工作 . 不带引号,$@
和$*
是等价的(而且几乎总是错误的) .那么,
$*
,$@
,"$*"
和"$@"
之间有什么区别?他们都与'all the arguments to the shell'有关,但他们做了不同的事情 . 如果没有引用,$*
和$@
会做同样的事情 . 他们将每个'word'(非空白序列)视为一个单独的参数 . 引用的表单完全不同,但是:"$*"
将参数列表视为单个空格分隔的字符串,而"$@"
几乎完全按照在命令行中指定的参数处理参数 . 当没有位置参数时,"$@"
根本没有扩展;"$*"
扩展为一个空字符串 - 是的,这是有区别的,虽然很难察觉它 . 在引入(非标准)命令al
之后,请在下面查看更多信息 .Secondary thesis: 如果您需要使用空格处理参数然后将它们传递给其他命令,那么您有时需要非标准工具来协助 . (或者你应该谨慎使用数组:
"${array[@]}"
的行为类似于"$@"
. )例:
为什么没有't that work? It doesn'工作,因为shell在扩展变量之前处理引号 . 所以,要让shell注意
$var
中嵌入的引号,你必须使用eval
:当你有文件名如“
He said, "Don't do this!"
”(带引号和双引号和空格)时,这会变得非常棘手 .shell(所有这些)都不会使这些东西特别容易处理,所以(有趣的是)很多Unix程序都不能很好地处理它们 . 在Unix上,文件名(单个组件)可以包含除斜杠和NUL
'\0'
之外的任何字符 . 但是,shell强烈建议路径名中的任何位置都不要有空格或换行符或制表符 . 这也是标准Unix文件名不包含空格等的原因 .在处理可能包含空格和其他麻烦字符的文件名时,你必须非常小心,我很久以前就发现我需要一个在Unix上不标准的程序 . 我称之为
escape
(版本1.1的日期为1989-08-23T16:01:45Z) .以下是使用
escape
的示例 - 使用SCCS控制系统 . 这是一个封面脚本,可以同时执行delta
(想想签到)和get
(想一想签出) . 各种参数,尤其是-y
(你进行更改的原因)将包含空格和换行符 . 请注意,脚本的日期是1992年,因此它使用反向标记而不是$(cmd ...)
表示法,并且在第一行不使用#!/bin/sh
.(这些天我可能不会非常彻底地使用escape - 例如
-e
参数不需要它 - 但总的来说,这是我使用escape
的一个更简单的脚本 . )escape
程序只是输出它的参数,而不像echo
那样,但是它确保参数受到保护以便与eval
一起使用(eval
的一个级别;我确实有一个执行远程shell执行的程序,并且需要逃避escape
的输出 .我有另一个名为
al
的程序,它每行列出一个参数(它更古老:版本1 . 1日期为1987-01-27T14:35:49) . 它在调试脚本时最有用,因为它可以插入命令行以查看实际传递给命令的参数 .[补充:现在为了显示各种
"$@"
符号之间的区别,这里还有一个例子:请注意,没有任何内容保留命令行上
*
和*/*
之间的原始空白 . 另请注意,您可以使用以下命令更改shell中的'command line arguments':这设置了4个选项,'
-new
', '-opt
', 'and
', and 'arg with space
' .]
嗯,这是一个相当长的答案 - 或许解释是更好的术语 . 可根据要求提供
escape
的源代码(发送电子邮件至gmail dot com的firstname dot lastname) .al
的源代码非常简单:就这样 . 它等同于Robert Gamble所展示的
test.sh
脚本,并且可以编写为shell函数(但是当我第一次编写al
时,本地版本的Bourne shell中不存在shell函数) .另请注意,您可以将
al
编写为简单的shell脚本:需要条件,以便在不传递参数时不产生输出 .
printf
命令将生成一个只有格式字符串参数的空行,但C程序不生成任何内容 .