在bash脚本中倒带stdin

Mar*_*cos 2 bash redirect stdin pointers pipe

有没有一种简单的方法可以/dev/stdin在我的bash脚本中"回放" 已经从输入管道读取全部或部分内容?

应用程序:我写了一个简单的MDA,在第1部分中,逐行读取来自fetchmail的单个电子邮件,如下所示:

while read -a linA; do
    echo -e "$[++linenum]:\t${#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
Run Code Online (Sandbox Code Playgroud)

在处理结束时,我希望将整个消息保存到一个文件中,用于调试,以便管道的编写者端看到它的整个输出已被读取,而不是返回失败(因此保留消息)在邮箱中未读).

Jon*_*ler 5

读管是破坏性的; 没有办法寻找管道:

ESPIPE (29): Illegal seek
Run Code Online (Sandbox Code Playgroud)

错误编号来自MacOS X,但名称是传统的(并且是POSIX强制的),并指示限制来自何处.

因此,如果要在shell脚本中重新处理输入,则需要将标准输入存储在文件中,以便可以重新处理它:

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
Run Code Online (Sandbox Code Playgroud)

要注意的唯一陷阱是,由于管道,while在父shell中无法访问循环中设置的变量.如果这是一个问题,您可以使用:

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

...reprocess $tmp.1 here...
}
Run Code Online (Sandbox Code Playgroud)

大括号将语句分组以进行I/O重定向.tee由于EOF,该过程将完成,因此当while read检测到EOF 时文件将完成,因此$tmp.1在循环后访问是安全的.

要注意的一件事是,tee这会使终端输入无响应.文件不会有问题; 管道输入不太可能成为问题.但是,tee可能会完全缓冲其输出,而不是对其输出进行行缓冲,因此在您键入多行输入之前,读取循环可能看不到任何内容.