在命令行中运行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
如果我们用引号运行它,它确实会得到正确的结果。但是引号是强制性的吗?
答案1
$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 知道不要使用通常的含义,即分隔参数。相反,它将变成文字空格。
其行为也没有什么不同"$@"
。它的存在正是为了保留引用的参数。
答案2
另一个答案很好。它解释了发生的事情:
hists aws s3 cp
扩展为history 15000 | grep "aws" "s3" "cp"
。grep
接收三个参数。的第一个参数
grep
是模式。其他参数是要在其中查找模式的文件。当前文件夹中grep
没有调用的文件s3
,因此显示错误消息。cp
grep
我的回答将允许您强化和修改该功能,以便hists aws s3 cp
可以工作。
原始代码中的第一个注释grep "$@"
允许您注入选项至grep
,像这样:
hists -i LS
你可能想这样做,也可能不想这样做。如果不想,那么这段代码应该是grep -- "$@"
。但即便如此,您仍可以将文件名传递给grep
。我估计您永远不会想grep
在函数中对文件进行操作。一些防止这种情况发生的机制会很有用。
如果您希望能够注入选项,那么没有什么简单的方法可以阻止您指定文件作为附加参数。该函数应该将多个参数传递给grep
;它本身不知道哪个是选项,哪个是文件。一些逻辑可能会处理这个问题,但让我们保持简单。
如果您可以不向 注入选项grep
,则可以确保该工具只获取一个参数,即模式。您可以使用grep -- "$1"
,但在这种情况下
hists aws s3 cp
将等同于hists aws
,其他参数无关紧要。这并非您想要的,但它将阻止grep
解析s3
或cp
抛出有关它们的错误。
或者你可以使用grep -- "$*"
。这是POSIX 的说法关于"$*"
:
当扩展发生在不会执行字段拆分的上下文中时,初始字段应连接起来形成单个字段,并且每个参数的值
IFS
如果IFS
包含至少一个字符则由变量的第一个字符分隔,如果未设置则由<space>分隔,如果设置为空字符串IFS
则没有分隔符。IFS
双引号内是“不会执行字段拆分的上下文”,标准IFS
以 <space> 开头。这意味着如果您使用grep -- "$*"
,则命令
hists aws s3 cp
将触发grep -- "aws s3 cp"
,这正是您首先想要的。请注意,使用以下命令您将获得相同的结果
hists aws s3 cp
尽管有多个空格。关键是hists
根本看不到这些空格。它将aws
、s3
和cp
视为单独的参数,然后 的机制"$*"
使用单个空格连接三个字符串。
另一方面,这些命令:
hists "aws s3 cp"
hists "aws s3 cp"
不管您使用grep -- "$*"
或grep -- "$@"
或,都会完全按照您的预期工作grep -- "$1"
。当 的参数较多时,这三种变体的行为会有所不同hists
;或较少(尝试hists
不带任何参数)。
因此,grep -- "$*"
在某些情况下,您可以将引号设为可选。grep
无论您输入什么,此变体也会阻止解析文件。