文件重定向与 dd

Mon*_*nad 4 bash dd pipe

假设我想将某个命令的输出通过管道传输到一个文件中。简单地……好吧,将它传送到一个文件,例如

some-command > file
Run Code Online (Sandbox Code Playgroud)

和管道它通过dd

some-command | dd $MAYBE_SOME_OPTIONS_LIKE_BS of=file
Run Code Online (Sandbox Code Playgroud)

这个问题主要是为了满足我的好奇心,除非后一种形式有一些不可忽视的好处或用例,否则在实践中我会省去练习并选择前者。

Kam*_*ski 5

直接访问 file

some-command > file
some-command | dd $MAYBE_SOME_OPTIONS_LIKE_BS of=file
Run Code Online (Sandbox Code Playgroud)

在第一种情况下,它some-command知道它写入file,因此它可以出于任何原因检索元数据(如 mtime 或 size)。此类元数据对于常规文件具有有用的意义,但对于未命名的管道则毫无意义。

some-command 甚至可能有兴趣打开其标准输出指向的内容进行阅读,这对于常规文件来说相当安全,显然在第二种情况下不是正确的做法。

但我认为这些场景非常罕见。


可查找与不可查找

的行为some-command可能取决于其标准输出是否可查找。尽管有可能,但通常情况并非如此。常规文件是可查找的(您可以在任何位置读/写)。管道不可查找。

dd本身就是一个有效的例子。考虑some-commanddd if=/dev/zero bs=1 count=1 seek=1。你的两个例子变成:

dd if=/dev/zero bs=1 count=1 seek=1 > file
dd if=/dev/zero bs=1 count=1 seek=1 | dd of=file
Run Code Online (Sandbox Code Playgroud)

第一个命令将起作用,它将在其中留下file两个空字节。在第二个命令中,第一个命令dd会抱怨它无法搜索,它将退出;第二个dd什么也得不到,什么都不写,然后无误地退出。

我承认seek这并不完全是“将某些命令的输出通过管道传输到文件中”。但通常some-command可以检测其标准输出是否可搜索,然后在实际搜索的情况下流式传输不同的输出。这个 shell 函数有点像一个概念证明:

is-seekable() {
  if </dev/null dd bs=1 count=0 seek=1 conv=notrunc 2>/dev/null; then
    echo seekable
  else
    echo non-seekable
  fi
  }
Run Code Online (Sandbox Code Playgroud)

尝试这些并在每个之后检查文件:

is-seekable > file
is-seekable | dd of=file
Run Code Online (Sandbox Code Playgroud)

了解以上所有内容,有一种情况我会考虑您的第二个命令:如果some-command在两种情况下确实表现不同(如我们的is-seekable函数)并且我希望它“认为”它正在写入管道,但我希望输出在一个文件中. 好的。虽然我宁愿使用cat而不是dd(这可能无关紧要)。例如,我可能想non-seekableis-seekable文件中获取并将其写入文件:

is-seekable | cat > file
Run Code Online (Sandbox Code Playgroud)

但前提是它有什么不同。否则,这将是(臭名昭著的)“无用使用cat”。或者在你的情况下“无用的使用dd”;如果您处理退出状态不佳,甚至是“有害使用”。


退出状态

现在试试这两个例子:

false > file
false | dd of=file
Run Code Online (Sandbox Code Playgroud)

如果您$?在第二个之后检查,您会发现它0(除非dd失败)。您的第二种情况丢弃了some-command(通常;例如在 Bash 调查中set -o pipefail,这是不可移植的)的退出状态。

因此,在第二种情况下some-command,无论出于何种原因都可能失败,而您(或者更确切地说是您的脚本的逻辑)将不知道。这足以避免不必要的管道dd(或根本不需要管道)。


权限

另一个细微差别:some-command > file使(子)shell 在some-command生成(执行到)之前打开文件;dd of=file代表自己打开文件。

有一些方法可以授予dd比任何其他“常规”命令更多的访问权限。换句话说,有一些方法可以让普通的dd行为更像是sudo dd没有密码。你不应该这样做,希望没有人这样做,但这是可能的。

您可以做的是按需授予访问权限:sudo dd. 在尝试写入受限文件时,它会很有用。分析一下:

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
Run Code Online (Sandbox Code Playgroud)

我认为这非常接近于“不可忽视的好处或用例”。


也许有一些选择

显然$MAYBE_SOME_OPTIONS_LIKE_BS可以改变dd行为。我知道像这样的选项conv=swab超出了范围,因为您希望file在两种情况下都接收相同的数据。

仍然有几个选项可以有所作为。例子:

  • status=progress 在处理每个输入块时,将在 stderr 上打印传输速率和容量统计信息;

  • oflag=noatime 不会更新文件的访问时间戳;

  • oflag=nolinks 如果文件有多个硬链接,则会失败;

  • 还有更多

上面的例子是 GNU 扩展,不可移植。便携dd的特点在这里


最后是KISS 原则:保持简单的愚蠢。你的第一个案例是愚蠢的简单。