Прочитайте введення в bash всередині циклу


97

У мене є сценарій bash, який є щось на зразок наступного,

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

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

Будь-який інший спосіб зробити те саме?


Те саме відбувається, коли ви перемикаєте користувача на bash і запускаєте команду read під комутованим користувачем у сценарії
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

55

Ви можете перенаправити звичайний stdin через блок 3, щоб зберегти його всередині конвеєра:

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

BTW, якщо ви дійсно використовуєте catцей спосіб, замініть його перенаправленням, і все стане ще простіше:

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

Або ви можете поміняти місцями stdin та блок 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це читання із завершеного сповіщенням, але якщо ви просто запустите так, як написано, read -u 3 inputце читання з консолі)?
Гордон Девіссон

4

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

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" на "для", який працює для мене. Спробуйте "sex -x", дивіться, звідки походить помилка
dimba

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

+1. Це було набагато простіше здійснити для моїх конкретних потреб, ніж інші пропозиції.
Натан Уоллес

1
На Вікі Wooledge див. Розділ Не читати рядки із «For». Також shellcheck.net попередження SC2013
Чарльз Даффі

3

Схоже, ви читаєте двічі, читання всередині циклу while не потрібне. Крім того, не потрібно викликати команду cat:

while read input
do
    echo $input
done < filename

4
Мета операційної програми полягає в тому, щоб читання всередині циклу надходило від користувача, тоді як зовнішнє - читати з файлу. Таким чином, вони законно хочуть двох різних читань із двох різних джерел. Це зрозуміло як з тексту запитання (описуючи внутрішню readповедінку як "неправильно [тому, що вона намагається прочитати з файлу filename"), так і їх прийняту відповідь.
Чарльз Даффі

1

Я знайшов цей параметр -u з read.

"-u 1" означає "читати зі stdin"

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.