перематывать стандартный ввод в скрипте bash - PullRequest
2 голосов
/ 16 февраля 2012

Есть ли простой способ "перемотать" /dev/stdin внутри моего bash-скрипта, который уже читает всю или некоторую часть из входного канала?

Приложение: я написал простой MDA, который в первой части читает построчно одно письмо из 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 ]

5 голосов
/ 16 февраля 2012

Чтение трубы разрушительно; нет возможности искать по трубе:

ESPIPE (29): Illegal seek

Номер ошибки взят из MacOS X, но имя является традиционным (и обязательным для POSIX) и указывает, откуда исходит ограничение.

Итак, если вы хотите повторно обработать ввод в сценарии оболочки, вам нужно спрятать стандартный ввод в файл, чтобы вы могли обработать его:

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

Единственная ловушка, которую нужно посмотреть, это то, что из-за конвейера переменные, установленные в цикле while, не доступны в родительской оболочке. Если это проблема, вы можете использовать:

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

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

Скобки группируют операторы для целей перенаправления ввода / вывода. Процесс tee будет завершен из-за EOF, поэтому файл будет завершен, когда while read обнаружит EOF, так что после цикла будет безопасно получить доступ к $tmp.1.

Единственное, на что нужно обратить внимание, это то, что tee сделает это безразличным к входу терминала. Файлы не будут проблемой; Вводный канал вряд ли будет проблемой. Однако tee, вероятно, будет полностью буферизовать свой вывод, а не строковую буферизацию своего вывода, поэтому цикл чтения может ничего не увидеть, пока вы не введете много строк ввода.

0 голосов
/ 19 мая 2014

Попробуйте exec < /dev/stdin, который может работать в Linux.

0 голосов
/ 16 февраля 2012

как насчет:

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

Я думаю, что это лучше, потому что вы читаете сообщение только один раз

0 голосов
/ 16 февраля 2012

Не совсем, нет.

Вам нужно будет просто добавить каждую строку в переменную, когда вы ее читаете, и очистить переменную по мере необходимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...