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

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

我有一个 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

答案1

$IFS您的方法是在用于分割读取的任何分隔符 ( ) 的空间中写入的每个内容中添加换行符。不要将其分解为换行符,而是将整个内容传递出去。您可以将上面的整个代码简化为:

 cat - > $file

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

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

答案2

要按字面意思读取文本文件,请勿使用 plain read,它以两种方式处理输出:

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

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

while IFS= read -r line; do …

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

要按字面意思编写字符串,不要只使用 plain echo,它以两种方式处理字符串:

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

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

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

这个只适合加工文本, 因为:

  • 大多数 shell 都会因输入中的空字符而阻塞。
  • 当你读完最后一行时,你无法知道末尾是否有换行符。 (如果输入不以换行符结尾,一些较旧的 shell 可能会遇到更大的问题。)

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

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

或者,更简单的是,

cat >|"$file"

在脚本中,您通常不需要使用>|sincenoclobber默认情况下处于关闭状态。

答案3

这将完全满足您的要求:

( 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

相关内容