在bash脚本中倒带stdin

时间:2012-02-16 13:35:27

标签: bash pointers redirect pipe stdin

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

应用程序:我写了一个简单的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

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

4 个答案:

答案 0 :(得分:5)

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

ESPIPE (29): Illegal seek

错误编号来自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

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

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

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

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

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

答案 1 :(得分:0)

不是,不。

您只需在阅读时将每一行附加到变量中,并根据需要清除变量。

答案 2 :(得分:0)

怎么样:

tmpfile=$(mktemp)
while read -a linA; do
  echo -e "$[++linenum]:\t${#linA[@]},${linA[*]}" > /dev/null  # verbose
  [ "${linA[0]}" = "Date:" ] && unset linA[0] && mailDate="${linA[*]}"
  echo "${linA}">>$tmpfile
done
mv $tmpfile fulltext.txt

我认为这是更好的方式,因为你只读过一次消息

答案 3 :(得分:0)

尝试可能在Linux下运行的exec < /dev/stdin