Чтение пользовательского ввода внутри цикла


99

У меня есть сценарий bash, который выглядит примерно так:

cat filename | while read line
do
    read input;
    echo $input;
done

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

Любой другой способ сделать то же самое?


То же самое происходит, когда вы переключаете пользователя в bash и запускаете команду чтения под переключенным пользователем в скрипте
krupal

Ответы:


106

Считайте с управляющего оконечного устройства:

read input </dev/tty

больше информации: http://compgroups.net/comp.unix.shell/Fixing-stdin-inside-a-redirected-loop


12
-1, так как это позволит обойти любое другое перенаправление. Например, bash yourscript < /foo/barбудет ждать ввода пользователя, это допустимо только при чтении паролей. Ответ @GordonDavisson предпочтительнее для всех других целей.
tiwo 06

56

Вы можете перенаправить обычный stdin через модуль 3, чтобы сохранить его внутри конвейера:

{ cat notify-finished | while read line; do
    read -u 3 input
    echo "$input"
done; } 3<&0

Кстати, если вы действительно используете catэтот способ, замените его перенаправлением, и все станет еще проще:

while read line; do
    read -u 3 input
    echo "$input"
done 3<&0 <notify-finished

Или вы можете поменять местами stdin и unit 3 в этой версии - прочтите файл с модулем 3 и просто оставьте stdin в покое:

while read line <&3; do
    # read & use stdin normally inside the loop
    read input
    echo "$input"
done 3<notify-finished

почему у тебя второй скрипт висит?
Лука Боррионе

2
@LucaBorrione: Как вы его используете? Ожидает ли он, что вы введете его (обратите внимание, что read lineчтение из notify-finished, но если вы просто запустите его, как написано, это read -u 3 inputбудет чтение с консоли)?
Гордон Дэвиссон

3

Похоже, вы читали дважды, чтение внутри цикла while не требуется. Кроме того, вам не нужно вызывать команду cat:

while read input
do
    echo $input
done < filename

4
Цель OP состоит в том, чтобы чтение внутри цикла происходило от пользователя, а внешнее - чтение из файла. Таким образом, они законно хотят два разных чтения из двух разных источников. Это ясно как из текста вопроса (описывающего readповедение внутреннего как «неправильное [потому что] он пытается читать из файла filename»), так и из принятого ими ответа.
Чарльз Даффи,

3

Попробуйте изменить цикл так:

for line in $(cat filename); do
    read input
    echo $input;
done

Модульный тест:

for line in $(cat /etc/passwd); do
    read input
    echo $input;
    echo "[$line]"
done

@ w2lame Протестировано снова, замените цикл while на цикл for - у меня работает. Попробуйте "sex -x" и посмотрите, откуда
взялась

4
не используйте кот, см. ответ Хай Ву
Фредрик Пил

+1. Это было намного проще реализовать для моих конкретных нужд, чем другие предложения.
Натан Уоллес,

1
См. Раздел « Не читать строки с помощью» в вики Wooledge. Кроме того , shellcheck.net предупреждение SC2013
Charles Duffy

1

Я нашел этот параметр -u при чтении.

"-u 1" означает "читать из стандартного ввода"

while read -r newline; do
    ((i++))
    read -u 1 -p "Doing $i""th file, called $newline. Write your answer and press Enter!"
    echo "Processing $newline with $REPLY" # united input from two different read commands.
done <<< $(ls)

-6
echo "Enter the Programs you want to run:"
> ${PROGRAM_LIST}
while read PROGRAM_ENTRY
do
   if [ ! -s ${PROGRAM_ENTRY} ]
   then
      echo ${PROGRAM_ENTRY} >> ${PROGRAM_LIST}
   else
      break
   fi
done
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.