Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
715 views
in Technique[技术] by (71.8m points)

pointers - rewinding stdin in a bash script

Is there a simple way to "rewind" /dev/stdin inside my bash script which already read all or some portion from the input pipe?

Application: I wrote a simple MDA that in part 1, reads a single email from fetchmail line by line, like so:

while read -a linA; do
    echo -e "$[++linenum]:${#linA[@]},${linA[*]}" > /dev/null  # verbose
    [ "${linA[0]}" = "Date:" ] && unset linA[0] && mailDate="${linA[*]}"
    [ "${linA[0]}" = "Subject:" ] && unset linA[0] && mailSubject="${linA[*]}"
    [ "$mailSubject" = "Courtesy Fill Notification" ] || break  # if wrong subject then thank you, we're done with this mail
done

and at the end of processing, I wish to save the entire message into a file, both for debugging, and so that the writer-side of the pipe sees that its entire output had been read, and not return failure (therefore keeping the message as unread in the mailbox).

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Reading a pipe is destructive; there is no way to seek on a pipe:

ESPIPE (29): Illegal seek

The error number is from MacOS X, but the name is traditional (and POSIX-mandated) and gives an indication of where the restriction comes from.

So, if you want to reprocess input in a shell script, you will need to stash the standard input away in a file so you can reprocess it:

tmp=${TMPDIR:-/tmp}/xx.$$    # Consider mktemp or something
trap "rm -f $tmp.1; exit 1" 0 1 2 3 13 15  # Exit, HUP, INT, QUIT, PIPE, TERM

tee $tmp.1 |
while read -a linA
do
    ...
done

...reprocess $tmp.1 here...

rm -f $tmp.1
trap 0
exit $exit_status

The only trap to watch is that because of the pipeline, variables set in the while loop are not accessible in the parent shell. If that's a problem, you can use:

tee $tmp.1 |
{
while read -a linA
do
    ...
done

...reprocess $tmp.1 here...
}

The braces group the statements for I/O redirection purposes. The tee process will have completed because of EOF so the file will be complete when the while read detects EOF, so it is safe to access $tmp.1 after the loop.

The one thing to watch is that tee will make this unresponsive to terminal input. Files won't be a problem; piped input is unlikely to be a problem. However, tee will probably fully buffer its output, rather than line buffering its output, so the read loop may not see anything until you've typed many lines of input.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...