some-command > restricted_file # doesn't work
sudo some-command > restricted_file # doesn't work
some-command | sudo dd > restricted_file # doesn't work
sudo sh -c 'some_command > restricted_file' # works, but it runs some_command as root
# you may not want this
some-command | sudo dd of=restricted_file # works
some-command | sudo tee restricted_file # works, more common, possibly with
# > /dev/null to suppress output to tty
直接访问
file
在第一种情况下,
some-command
知道它写入file
,因此它可以出于任何原因检索元数据(如 mtime 或 size)。这样的元数据对于普通文件很有用,但对于未命名的管道来说,它们毫无意义。some-command
甚至可能有兴趣打开它的 stdout 指向的内容以供阅读,这对于常规文件来说是相当安全的,并且在第二种情况下显然不是正确的做法。但我认为这些情况非常罕见。
可搜索与不可搜索
的行为
some-command
可能取决于它的标准输出是否可搜索。通常情况并非如此,尽管有可能。常规文件是可搜索的(您可以在任何位置读/写)。管道是不可搜索的。dd
本身就是一个有效的例子。考虑some-command
是dd if=/dev/zero bs=1 count=1 seek=1
。你的两个例子变成:第一个命令会起作用,它会留下
file
两个空字节。在第二个命令中,第一个命令dd
会抱怨它无法寻找,它会退出;第二个dd
什么也得不到,什么也不写,然后无错退出。我承认
seek
这并不是完全简单的“将某些命令的输出通过管道传输到文件中”。但通常some-command
可以检测其标准输出是否可搜索,然后在不实际搜索的情况下流式传输不同的输出。这个 shell 函数有点像概念验证:尝试这些并在每一个之后检查文件:
了解以上所有内容后,有一种情况我会考虑您的第二个命令:如果
some-command
在两种情况下确实表现不同(例如我们的is-seekable
函数)并且我希望它“认为”它正在写入管道但我想要文件中的输出. 好的。尽管我宁愿使用cat
而不是dd
(这可能无关紧要)。例如,我可能想non-seekable
从文件中获取is-seekable
并将其写入文件:但前提是它有任何不同。否则它将是(在)著名的“无用的使用
cat
”。或者在你的情况下“无用的使用dd
”;如果您处理退出状态不当,甚至是“有害使用”。退出状态
现在试试这两个例子:
如果你
$?
在第二个之后检查,你会发现它是0
(除非dd
失败)。您的第二个案例丢弃的退出状态some-command
(通常;例如,在 Bash investigateset -o pipefail
中,这是不可移植的)。因此,在第二种情况下
some-command
,无论出于何种原因都可能会失败,而您(或者更确切地说是脚本的逻辑)将不知道。这足以避免不必要的管道dd
(或根本不需要管道)。权限
另一个细微差别:
some-command > file
使(子)shell 在some-command
生成(执行到)之前打开文件;dd of=file
代表自己打开文件。有一些方法可以授予
dd
比任何其他“常规”命令更多的访问权限。换句话说,有一些方法可以让普通的dd
行为更像sudo dd
没有密码。你不应该这样做,希望没有人这样做,但这是可能的。您可以做的是按需授予访问权限:
sudo dd
. 它在尝试写入受限文件时很有用。分析一下:我认为这非常接近“不可忽视的好处或用例”。
也许有一些选择
显然
$MAYBE_SOME_OPTIONS_LIKE_BS
可以改变dd
行为。我知道像这样conv=swab
的选项超出了范围,因为您想file
在两种情况下都接收相同的数据。仍然有一些选择可以有所作为。例子:
status=progress
在处理每个输入块时,将在 stderr 上打印传输速率和体积统计信息;oflag=noatime
不会更新文件的访问时间戳;oflag=nolinks
如果文件有多个硬链接,将会失败;等等。_
上面的示例是 GNU 扩展,不可移植。便携
dd
是这里的特点。吻
最后是KISS 原则:保持简单。你的第一个案例很简单。
任何用户都不应注意到任何差异。从性能的角度来看,怀疑也会有差异。如果您同时考虑这两种情况,则各有利弊,但这在很大程度上取决于您要实现的目标。
例如:cat 文件 | dd of=file2 vs cat file > file2. 这里的区别是,cat 将打开一个文件,dd 将继承流,除非您指定块大小或某种写入标志,它将立即开始写入 fs 堆栈。
如果你不使用 dd 的任何功能,你几乎只是分叉一个中间人,添加一个进程,我认为这个双分叉与只让 dd 处理 if=file of=newfile 相比性能损失最小。在这种情况下,优点是您可以在 dd 中查找/跳过,而且只有一个进程。
如果您严格来说只是文件重定向与 dd,我会说如果您不为 dd 提供任何标志/参数,则没有区别。在这种情况下,您可以将其用作中间人缓冲区,但还有更好的选择,例如:cat file | 缓冲区 | gzip > gzip 内容。例如,上面的堆栈调用,假设你有一个超快的源和一个慢速的目的地,中间有缓冲区,你可以确保接收方始终有一个繁忙的输入队列,如果你正在压缩,你可以确保让你的 CPU 不等待 IO。
同样,所有边缘情况都需要单独的问题,但如您所见,如果我们严格地从一个到另一个来说,即使存在差异,也怀疑除了极端边缘情况外它是否会被注意到。
希望这会有所帮助,我不得不说这个问题很有趣,我花了一些时间来考虑。