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


15

我有一个bash函数,该函数将文件作为参数,验证文件是否存在,然后将标准输入中的所有内容写入文件中。天真的解决方案适用于文本,但我在使用任意二进制数据时遇到问题。

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

Answers:


15

您的方法是在任何分隔符($IFS)用于分隔读取的空间中,将断行符添加到它写入的所有内容中。无需将其分解为换行符,只需将整个过程传递下去。您可以将上面的整个代码缩减为:

 cat - > $file

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

编辑:如果您使用的是zsh,则可以> $file代替猫使用。您将重定向到文件并截断​​它,但是如果有任何东西挂在那儿,等待某些东西接受STDIN,它将在那一点被读取。我认为您可以使用bash进行类似的操作,但是必须设置一些特殊模式。


我无法使stdin重定向示例正常工作,但是将cat示例更改为> |。(我有noclobber套装)的工作原理很吸引人。感谢您度过美好的一天。^
David Souther

无猫版+1。始终避免使用无用的猫;)
rozcietrzewiacz 2011年

@rozcietrzewiacz:是的,除了那是事后的想法,我错了。这可能不是猫的无用用法。您可能唯一能做的就是> $file。这仅是在父shell脚本中查找stdin的第一件事。基本上,David的所有代码都可以简化为一个字符,但是我认为该代码cat -更优雅,麻烦也更少,因为它是可见的。
卡莱布(Caleb)

有时我cat
会把

@MichaelMrozek:有时我cat只是为数据文件命名,所以坚持使用数据文件的人必须进行精神体操来阅读代码。命名管道也是很好的目标。
卡莱布(Caleb)

7

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

  • read解释\为转义字符;用于read -r关闭此功能。
  • read在文字上分成单词$IFS; 设置IFS为空字符串可将其关闭。

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

while IFS= read -r line; do 

有关此惯用语的说明,请参见为什么while IFS= read经常使用而不是IFS=; while read..

要从字面上编写字符串,请不要仅使用plain echo,它会以两种方式处理字符串:

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

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

printf %s "$line"
printf '%s\n' "$line"

这仅适用于处理文本,因为:

  • 大多数外壳会在输入中的空字符处阻塞。
  • 阅读完最后一行后,您将无法知道结尾是否有换行符。(如果输入不以换行符结尾,则某些较旧的shell可能会遇到更大的麻烦。)

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

: >| "$file"
cat >>"$file"

或者,更简单地说,

cat >|"$file"

在脚本中,>|由于noclobber默认情况下处于关闭状态,因此通常无需使用。


感谢您指出xpg_echo,这实际上是我在代码中的其他地方甚至没有意识到的问题。关于noclobber,我习惯在我的bashrc中将其打开。
David Souther

0

这将完全满足您的要求:

( while read -r -d '' ; do
    printf %s'\0' "${REPLY}" ;
  done ;

  # When read hits EOF, it returns non-zero which exits the while loop.
  # That data still needs to be output:
  printf %s "${REPLY}"
) >> ${file}

不过要注意内存使用情况。这将以空分隔的方式读取输入。

如果输入中没有\0 字节,则bash首先需要将输入的全部内容读入内存,然后将其输出。

关于截断步骤:

echo -n '' >| "$file" #Truncate the file

一个简单得多的等效项是:

> ${file}   #Truncate the file
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.