bash
can't hold binary data in its variables. It's already bad enough to process text with shell loops, it would be even worse for processing binary data. The shell is the tool to run other tools.
Also note that the read
buit-in command reads characters, not bytes.
Also, dd
does one read
system call, so a dd bs=77 count=1
won't necessarily read 77 bytes especially if stdin is a pipe (the GNU implementation of dd
has iflag=fullblock
for that).
Here, you want to use a data processing programming language like perl
:
In perl
:
perl -ne 'BEGIN{$/=\77}
print "Do something with the 77 byte long <$_> record\n"'
With GNU awk
:
LC_ALL=C awk -vRS='.{,77}' '{print "the record is in <" RT ">"}'
If you want to use a shell, your best option is probably zsh
which is the only one that can store binary data in its variables:
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
If all you want to do is pass each chunk as stdin to a new invocation of some command
, then you can use GNU split
and its --filter
option:
split -b 77 --filter='some command'
--filter
starts a new shell to evaluate some command
for each chunk. Unless your sh
does the optimisation already by itself, you can do:
split -b 77 --filter='exec some command'
To save a fork.
Using dd
, you could parse its stderr output to find out the end of input. You'd need the GNU specific iflag=fullblock
as well:
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
If the input size is multiple of 77 though, some command
will be run an extra time with an empty input.
bash
doesn't support storing binary data in its variables. You'll need a shell that does likezsh
, or better use a programming language likeperl
.NUL
byte.. Actually problem is detecting when pipe was closed. I can read that data withdd
or any tool that is able to do magic with binary data. If I can use something likeselect()
orpoll()
directly in Bash, I can usedd
to solve problem.