如何在 bash 管道中处理原始二进制数据?

Dav*_*her 16 shell bash binary

我有一个 bash 函数,它将文件作为参数,验证文件是否存在,然后将来自 stdin 的任何内容写入文件。天真的解决方案适用于文本,但我遇到了任意二进制数据的问题。

echo -n '' >| "$file" #Truncate the file
while read lines
do  # Is there a better way to do this? I would like one...
    echo $lines >> "$file"
done
Run Code Online (Sandbox Code Playgroud)

Cal*_*leb 15

您的方法是为它在任何分隔符 ( $IFS) 用于拆分读取的空间中写入的所有内容添加换行符。与其将其分解成换行符,不如将整个内容传递下去。您可以将上面的全部代码缩减为:

 cat - > $file
Run Code Online (Sandbox Code Playgroud)

您不需要截断位,这将截断并将整个 STDIN 流写入其中。

编辑:如果您使用的是 zsh,则可以> $file代替 cat 使用。您正在重定向到一个文件并截断​​它,但是如果有任何东西在那里等待接受 STDIN,它将在那时被读取。我认为你可以用 bash 做这样的事情,但你必须设置一些特殊的模式。


Gil*_*il' 7

要从字面上读取文本文件,请不要使用 plain read,它以两种方式处理输出:

  • read解释\为转义字符;用于read -r关闭此功能。
  • read在 中的字符上拆分为单词$IFS;设置IFS为空字符串以关闭此功能。

逐行处理文本文件的常用习惯用法是

while IFS= read -r line; do …
Run Code Online (Sandbox Code Playgroud)

有关此习语的解释,请参阅为什么while IFS= read如此频繁地使用,而不是IFS=; while read...

要从字面上写一个字符串,不要只使用 plain echo,它以两种方式处理字符串:

  • 在某些 shell 上,echo进程会转义反斜杠。(在 bash 上,这取决于是否xpg_echo设置了该选项。)
  • 一些字符串被视为选项,例如-n-e(确切的设置取决于外壳)。

从字面上打印字符串的可移植方式是使用printf. (在 bash 中没有更好的方法,除非您知道您的输入看起来不像是 的选项echo。)使用第一种形式打印确切的字符串,如果要添加换行符,则使用第二种形式。

printf %s "$line"
printf '%s\n' "$line"
Run Code Online (Sandbox Code Playgroud)

这仅适用于处理text,因为:

  • 大多数 shell 会因输入中的空字符而窒息。
  • 当您阅读最后一行时,您无法知道末尾是否有换行符。(如果输入不以换行符结尾,一些较旧的 shell 可能会遇到更大的麻烦。)

您无法在 shell 中处理二进制数据,但大多数 unice 上的现代版本实用程序可以处理任意数据。要将所有输入传递到输出,请使用cat. 切线echo -n ''是一种复杂且不可移植的无所事事方式;echo -n将同样好(或不取决于外壳),并且:更简单且完全可移植。

: >| "$file"
cat >>"$file"
Run Code Online (Sandbox Code Playgroud)

或者,更简单,

cat >|"$file"
Run Code Online (Sandbox Code Playgroud)

在脚本中,您通常不需要使用,>|因为noclobber默认情况下是关闭的。