Odczytaj dane wejściowe użytkownika wewnątrz pętli

99

Mam skrypt bash, który przypomina śledzenie

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

ale to wyraźnie nie daje mi właściwego wyniku, ponieważ kiedy czytam w pętli while, próbuje odczytać z nazwy pliku pliku z powodu możliwego przekierowania we / wy.

Jakiś inny sposób na zrobienie tego samego?

w2lame
źródło
To samo dzieje się, gdy przełączasz użytkownika w bashu i uruchamiasz polecenie odczytu pod przełączonym użytkownikiem w skrypcie
krupal

Odpowiedzi:

106

Odczytaj z kontrolnego urządzenia końcowego:

read input </dev/tty

więcej informacji: http://compgroups.net/comp.unix.shell/Fixing-stdin-inside-a-redirected-loop

wilgotny
źródło
12
-1, ponieważ spowoduje to obejście wszelkich innych przekierowań. Na przykład bash yourscript < /foo/barbędzie czekać na wprowadzenie danych przez użytkownika, jest to dopuszczalne tylko podczas odczytu haseł. Odpowiedź udzielona przez @GordonDavisson jest preferowana do wszystkich innych zastosowań.
drugi
56

Możesz przekierować zwykłe stdin przez jednostkę 3, aby zachować ją w potoku:

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

BTW, jeśli naprawdę używasz cattego sposobu, zamień go na przekierowanie, a wszystko stanie się jeszcze prostsze:

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

Lub możesz zamienić stdin i jednostkę 3 w tej wersji - przeczytaj plik za pomocą jednostki 3 i po prostu zostaw stdin w spokoju:

while read line <&3; do
    # read & use stdin normally inside the loop
    read input
    echo "$input"
done 3<notify-finished
Gordon Davisson
źródło
dlaczego twój drugi scenariusz się zawiesił?
Luca Borrione,
2
@LucaBorrione: Jak go używasz? Czy czeka, aż podasz mu dane wejściowe (zauważ, że read lineczyta z notify-finish, ale jeśli po prostu uruchomisz to tak, jak napisano, read -u 3 inputczyta z konsoli)?
Gordon Davisson,
3

Wygląda na to, że przeczytałeś dwukrotnie, odczyt wewnątrz pętli while nie jest potrzebny. Nie musisz też wywoływać polecenia cat:

while read input
do
    echo $input
done < filename
Hai Vu
źródło
4
Celem OP jest, aby odczyt wewnątrz pętli pochodził od użytkownika, podczas gdy zewnętrzny - odczyt z pliku. Dlatego słusznie chcą dwóch różnych odczytów z dwóch różnych źródeł. Wynika to jasno zarówno z treści pytania (opisującego zachowanie wewnętrznego readjako „nie w porządku [ponieważ] próbuje odczytać z pliku filename”), jak i przyjętej odpowiedzi.
Charles Duffy,
3

Spróbuj zmienić pętlę w ten sposób:

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

Test jednostkowy:

for line in $(cat /etc/passwd); do
    read input
    echo $input;
    echo "[$line]"
done
dimba
źródło
@ w2lame Przetestowano ponownie, zmień pętlę „while” na pętlę „for” - działa dla mnie. Spróbuj „sex -x”, zobacz, skąd bierze się błąd
dimba
4
nie używaj kota, zobacz odpowiedź od Hai Vu
Fredrik Pihl
+1. Było to znacznie łatwiejsze do wdrożenia dla moich konkretnych potrzeb niż inne sugestie.
Nathan Wallace,
1
Zobacz temat Nie czytaj wierszy z literą For na wiki Wooledge. Również shellcheck.net ostrzeżenie SC2013
Charles Duffy
1

Znalazłem ten parametr -u z odczytem.

„-u 1” oznacza „czytaj ze standardowego wejścia”

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)
xerostomus
źródło
-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
Munchk1n
źródło