我正在寻找类似while IFS= read -r -n $length str; do ... done但二进制数据的东西。是否可以使用dd或其他工具来做到这一点?是否有一些技术可以使这些工具能够看到管道(实际从中读取的标准输入)何时关闭并终止循环?
目前我编码和解码二进制数据并使用read但它太慢了..( base64 | while read -r -n77 str; do echo $str | base64 -d; ... done)
bash不能在其变量中保存二进制数据。用shell循环处理文本已经够糟糕的了,处理二进制数据会更糟糕。shell 是运行其他工具的工具。
另请注意,read内置命令读取的是字符,而不是字节。
此外,dd执行一次 read系统调用,因此 add bs=77 count=1不一定会读取 77 个字节,特别是如果 stdin 是管道(GNU 实现dd具有iflag=fullblock)。
在这里,您想使用一种数据处理编程语言,例如perl:
在perl:
perl -ne 'BEGIN{$/=\77}
print "Do something with the 77 byte long <$_> record\n"'
Run Code Online (Sandbox Code Playgroud)
使用 GNU awk:
LC_ALL=C awk -vRS='.{,77}' '{print "the record is in <" RT ">"}'
Run Code Online (Sandbox Code Playgroud)
如果你想使用一个 shell,你最好的选择可能zsh是唯一一个可以在其变量中存储二进制数据的选项:
while LC_ALL=C IFS= read -ru0 -k77 record; do
print -r -- "you may only call builtins with $record
anyway since you can't pass NUL bytes in arguments
to an external command"
done
Run Code Online (Sandbox Code Playgroud)
如果您只想将每个块作为标准输入传递给 的新调用some command,那么您可以使用 GNUsplit及其--filter选项:
split -b 77 --filter='some command'
Run Code Online (Sandbox Code Playgroud)
--filter启动一个新的 shell 来评估some command每个块。除非您自己sh进行优化,否则您可以执行以下操作:
split -b 77 --filter='exec some command'
Run Code Online (Sandbox Code Playgroud)
为了节省叉子。
使用dd,您可以解析其 stderr 输出以找出输入的结尾。您还需要特定于 GNU 的iflag=fullblock:
while
{
report=$({
LC_ALL=C dd bs=77 iflag=fullblock count=1 2>&3 |
some command >&4 3>&- 4>&-
} 3>&1)
} 4>&1
[ "${report%%+*}" -eq 1 ]
do
: nothing
done
Run Code Online (Sandbox Code Playgroud)
但是,如果输入大小是 77 的倍数,some command则会以空输入运行额外的时间。