该命令单独运行时会产生预期结果(crontab 的最后一行):
tail -n 1 /etc/crontab
但是,当我将其作为 echo 命令的一部分运行以将结果发送到文件时,它会添加工作目录中所有文件的摘要以及预期结果:
sudo bash -c 'echo $(tail -n 1 /etc/crontab) > /path/to/file'
为什么这个命令会产生额外的数据?
答案1
您的 crontab 行*
中有一个或多个星号,表示“任何时间”。当从命令替换中替换该行时,结果类似于
echo * * * * * cmd > /path/to/file
尽管最多进一步的扩展不适用于命令替换的输出,路径名扩展是(与字段分割一样):
命令替换的结果不应被处理以进行进一步的波形符扩展、参数扩展、命令替换或算术扩展。如果命令替换发生在双引号内,则字段分割和路径名扩展不得对替换结果执行。
路径名扩展会变成*.txt
匹配文件名列表(通配符),其中*
匹配所有内容。最终结果是您获得*
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'
这应该可以满足您的要求,并且也更简单(唯一的其他实质性区别是该版本将省略原本会发生的字段分割,因此空间的运行不会被折叠)。
答案2
让我们考虑一个包含这些文件的目录:
$ 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*
如果没有双引号,则结果命令替换被外壳扩展。其中一项扩展是路径名扩展。在上面的情况下,这意味着f*
扩展以匹配以f
.
除非您明确想要 shell 扩展,否则请将所有 shell 变量和/或命令替换放在双引号内。
答案3
globing shell 机制将扩展*
到本地文件。
crontab 行可能有一个*
as 占位符。
例如,crontab 中的这一行在周日上午 7.47 运行,第一个星表示任何一天,第二个星表示任何月。
47 7 * * 0 /run/on/sunday
然后你tail
,并发出
echo 47 7 * * 0 /run/on/sunday
这将扩展*
到本地文件。