Reading from a file descriptor
- #builtins
- #bash-compat
mapfile reads lines into an array. Usually from standard input — but bash lets
you point it at any open file descriptor, which is how you read from one file
while a loop is already consuming another. huck didn't support that:
# before
$ exec 3< data.txt
$ mapfile -t -u 3 A; echo "n=${#A[@]}"
huck: line 1: mapfile: -u: invalid option
n=0# after
$ exec 3< data.txt
$ mapfile -t -u 3 A; echo "n=${#A[@]}"
n=3That option had been deliberately rejected rather than ignored. A while back it briefly parsed and then did nothing, which meant you got an empty array and no error — worse than the rejection, so the rejection went back in until someone implemented it. That's now done, along with its two companions.
The callback, and three wrong guesses
mapfile -C CALLBACK -c N runs a command every N lines while it reads — useful
for progress on a big file:
# after
$ mapfile -t -C "echo read so far:" -c 2 A < data.txt
read so far: 1 betaSimple enough to describe. I got it wrong three times before measuring, and each mistake is the kind that would have shipped looking fine:
It's a command line, not arguments. bash appends the line number and the
line to your callback text and runs the whole thing. So $1 inside your callback
is not the index — it's empty, and the index arrives glued onto the end of
whatever your command prints. That's why the output above reads
read so far: 1 beta.
It fires before the line is stored, not after. With -c 2 over five lines
it fires at lines 1 and 3 — so if your callback looks at the array, it's one
element short of what you'd expect.
The default matters more than it looks. bash's default interval is 5000
lines, so -C with no -c fires nothing at all on ordinary files. My first
version fired on the very first line, which looks harmless until you realise it
means every single -C user gets a spurious callback.
The third one is the one I'd have shipped. It only shows up if you test -C
without -c, and there's no reason to think of that unless you've read what
the default is.
While we were nearby
exec was missing half its error message:
# before # after
$ exec -Q $ exec -Q
exec: -Q: invalid option exec: -Q: invalid option
exec: usage: exec [-cl] [-a name] [command [argument ...]] [redirection ...]Everything else about exec's option handling was already right — it was only
the second line, the one that tells you what the valid options actually are.