在命令行运行history
时,结果是这样的:
$history aws s3 cp
16160 aws s3 cp s3://mybucket/air2008/10 .
16254 hists "aws s3 cp"
对于hists
旨在成为上述内容的快捷方式的功能:
type hists
hists is a function
hists ()
{
history 15000 | grep "$@"
}
运行时我们得到
09:40:44/csv $hists aws s3 cp
grep: s3: No such file or directory
grep: cp: No such file or directory
如果我们用引号运行它,那么它确实会得到正确的结果。但是引号是强制性的吗?
$hists "aws s3 cp"
“扩展”到history 15000 | grep "aws s3 cp"
. 这意味着grep
接收一个参数。$hists aws s3 cp
扩展为history 15000 | grep "aws" "s3" "cp"
。grep
接收三个参数。第一个参数
grep
是模式。其他参数grep
是要在其中查找模式的文件。当前文件夹中没有名为s3
或cp
的文件,因此grep
显示错误消息。引号可以改变命令行中文本的含义。如果您希望参数包含空格,引号是实现此目的的一种方法。
$hosts aws\ s3\ cp
做同样的事情:通过在空格前添加反斜杠,shell 知道不使用通常的含义,即分隔参数。相反,它将变成一个字面的空白。与 . 的行为也没有区别
"$@"
。它的存在正是为了保留引用的参数。另一个答案很好。它解释了发生了什么:
我的回答将允许您加固和修改该功能,以便
hists aws s3 cp
正常工作。原始代码中的第一个注释
grep "$@"
允许您向 注入选项,grep
如下所示:您可能希望也可能不希望能够做到这一点。如果不是,那段代码应该是
grep -- "$@"
. 但即便如此,您也可以将文件名传递给grep
. 我希望您永远不会希望grep
在函数中对文件进行操作。一些防止这种情况的机制会很有用。如果您希望能够注入选项,没有简单的方法可以阻止您将文件指定为附加参数。该函数应将多个参数传递给
grep
;它自己不知道哪个是选项,哪个是文件。有些逻辑可能会处理这个问题,但让我们保持简单。如果您可以在不向 注入选项的情况下生活
grep
,则可以确保该工具只获得一个参数,即模式。您可以使用grep -- "$1"
,但在这种情况下将等同于
hists aws
,附加参数无关紧要。这不是你想要的,但它会阻止grep
解析s3
和cp
/或抛出关于它们的错误。或者你可以使用
grep -- "$*"
. 这就是POSIX 所说的"$*"
:双引号内是“不进行字段拆分的上下文”,标准
IFS
以 <space> 开头。这意味着如果您使用grep -- "$*"
, 命令将触发
grep -- "aws s3 cp"
,这正是您首先想要的。请注意,您将获得相同的结果尽管有多个空间。关键是
hists
根本看不到这些空间。它将aws
,s3
和cp
作为单独的参数,然后"$*"
使用单个空格连接三个字符串的机制。另一方面,这些命令:
将完全按照您的预期工作,无论您使用
grep -- "$*"
orgrep -- "$@"
还是grep -- "$1"
. 当 ; 有更多参数时,这三个变体的行为不同hists
;或更少(尝试hists
不带任何参数)。因此,
grep -- "$*"
在某些情况下,您可以将引号设为可选。此变体还将阻止grep
解析文件,无论您键入什么。