回显tail命令会产生意外的输出?


8

此命令单独运行时,将产生预期的结果(crontab的最后一行):

tail -n 1 /etc/crontab

但是,当我将其作为echo命令的一部分运行以将结果发送到文件时,它会添加工作目录中所有文件的摘要以及预期结果:

sudo bash -c 'echo $(tail -n 1 /etc/crontab) > /path/to/file'

为什么此命令产生额外的数据?



3
echo在这里为您做什么?还考虑tail -n 1 /etc/crontab | sudo tee /path/to/file >/dev/null
ctrl-alt-delor

Answers:


22

您的crontab行中有一个或多个星号*,表示“随时”。当从命令替换中替换该行时,结果类似于

echo * * * * * cmd > /path/to/file

尽管大多数扩展都不适用于命令替换的输出,但路径名扩展(字段拆分也是如此)

命令替换的结果不得用于进一步的波浪号扩展,参数扩展,命令替换或算术扩展。如果在双引号内发生命令替换,则不应对替换结果执行字段拆分和路径名扩展

路径名扩展是*.txt一个匹配的文件名列表(globbing),在那里*匹配所有内容。最终结果是,您*在crontab行中的每个目录中都列出了工作目录中的每个(非隐藏)文件名。


如果发布的代码代表更复杂的命令,则可以通过引用扩展来解决此问题:

sudo bash -c 'echo "$(tail -n 1 /etc/crontab)" > /path/to/file'

但更直接地就是echo完全失去:

sudo bash -c 'tail -n 1 /etc/crontab > /path/to/file'

这应该可以实现您想要的并且更简单(唯一的实质区别是此版本将省略原本会发生的字段拆分,因此不会折叠空格)。


5
由于/ etc / crontab几乎始终是世界可读的,因此实际上不需要所有“ bash -c”技巧:这 tail -n -1 /etc/crontab | sudo tee /path/to/file是我发现的将输出重定向到需要超级用户特权的文件时最不容易出错的惯用法。
低音

5

让我们考虑一个包含这些文件的目录:

$ ls
crontab  file1  file2  file3
$ cat crontab
f*

现在,让我们运行tail命令:

$ tail -n 1 crontab
f*

上面是最后一行,crontab这就是我们的期望。然而:

$ echo $(tail -n 1 crontab)
file1 file2 file3

双引号消除了此问题:

$ echo "$(tail -n 1 crontab)"
f*

如果不使用双引号,则Shell会扩展命令替换的结果。扩展之一是路径名扩展名。在上述情况下,这意味着将f*扩展为匹配以开头的每个文件名f

除非您明确需要shell扩展,否则请将所有的shell变量和/或命令替换都放在双引号中。


4

globing shell机制将扩展*到本地文件。

crontab行可能有*任何占位符。

例如,crontab中的该行在星期日的上午7.47运行,第一颗星表示任何一天,第二颗表示任何月份。

47  7 * * 0 /run/on/sunday

然后你tail,并发出

echo 47  7 * * 0 /run/on/sunday

将扩展*为本地文件。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.