简体   繁体   English

在bash脚本中倒退stdin

[英]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? 有没有一种简单的方法可以在我的bash脚本中“倒带” /dev/stdin ,该脚本已经从输入管道中读取了全部或部分内容?

Application: I wrote a simple MDA that in part 1, reads a single email from fetchmail line by line, like so: 应用程序:我编写了一个简单的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

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). 并且在处理结束时,我希望将整个消息保存到文件中,以进行调试,以便管道的编写器端看到其整个输出已被读取,而不是返回失败(因此保留消息)邮箱中的未读邮件)。

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. 错误号来自MacOS X,但名称是传统名称(由POSIX规定),并指出了限制的来源。

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: 因此,如果要在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

The only trap to watch is that because of the pipeline, variables set in the while loop are not accessible in the parent shell. 唯一需要注意的陷阱是由于管道的原因, while循环中设置的变量无法在父外壳中访问。 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. 花括号将语句分组以用于I / O重定向。 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. tee进程将由于EOF而完成,因此当while read检测到EOF时文件将完成,因此可以安全地在循环后访问$tmp.1

The one thing to watch is that tee will make this unresponsive to terminal input. 要注意的一件事是, tee将使其对终端输入无响应。 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. 但是, tee可能会完全缓冲其输出,而不是使用行缓冲其输出,因此在您键入很多行输入之前,read循环可能看不到任何内容。

尝试exec < /dev/stdin ,它可能在Linux下工作。

Not really, no. 不是,不是

You'll just have to append each line into a variable as you read it and clear the variable as needed. 您只需在阅读时将每行追加到变量中,然后根据需要清除该变量。

how about: 怎么样:

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

I think it's better way because you read message just once 我认为这是更好的方法,因为您只阅读了一次消息

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM