Lesen Sie die Eingabe in Bash innerhalb einer while-Schleife

97

Ich habe ein Bash-Skript, das ungefähr so ​​aussieht:

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

Dies gibt mir jedoch eindeutig nicht die richtige Ausgabe, da beim Lesen in der while-Schleife aufgrund der möglichen E / A-Umleitung versucht wird, aus dem Dateinamen der Datei zu lesen.

Gibt es eine andere Möglichkeit, dasselbe zu tun?

w2lame
quelle
Das Gleiche passiert, wenn Sie den Benutzer in Bash wechseln und den
Lesebefehl

Antworten:

106

Lesen Sie vom steuernden Endgerät:

read input </dev/tty

Weitere Informationen: http://compgroups.net/comp.unix.shell/Fixing-stdin-inside-a-redirected-loop

danke
quelle
12
-1, da dies jede andere Weiterleitung umgeht. Zum Beispiel bash yourscript < /foo/barfür die Benutzereingabe wartet, dann ist dies nur dann akzeptabel , wenn Passwörter zu lesen. Die Antwort von @GordonDavisson ist für alle anderen Zwecke vorzuziehen.
Zwei
55

Sie können den regulären Standard durch Einheit 3 ​​umleiten, um ihn in der Pipeline zu behalten:

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

Übrigens, wenn Sie catdiese Methode wirklich verwenden , ersetzen Sie sie durch eine Weiterleitung, und die Dinge werden noch einfacher:

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

Oder Sie können stdin und unit 3 in dieser Version austauschen - lesen Sie die Datei mit unit 3 und lassen Sie stdin einfach in Ruhe:

while read line <&3; do
    # read & use stdin normally inside the loop
    read input
    echo "$input"
done 3<notify-finished
Gordon Davisson
quelle
Warum hängt dein zweites Skript?
Luca Borrione
2
@ LucaBorrione: Wie benutzt du es? Wartet es darauf, dass Sie ihm eine Eingabe geben (beachten Sie, dass read linedas Lesen von Benachrichtigung beendet ist, aber wenn Sie es nur so ausführen, wie es geschrieben wurde, read -u 3 inputwird von der Konsole gelesen)?
Gordon Davisson
4

Versuchen Sie, die Schleife folgendermaßen zu ändern:

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

Gerätetest:

for line in $(cat /etc/passwd); do
    read input
    echo $input;
    echo "[$line]"
done
dimba
quelle
@ w2lame Erneut getestet, ändern Sie "while" -Schleife in "for" -Schleife - funktioniert für mich. Versuchen Sie "sex -x" zu sehen, woher der Fehler kommt
dimba
4
Verwenden Sie keine Katze, siehe Antwort von Hai Vu
Fredrik Pihl
+1. Dies war für meine spezifischen Bedürfnisse viel einfacher umzusetzen als andere Vorschläge.
Nathan Wallace
1
Siehe Lesen Sie keine Zeilen mit For im Wooledge-Wiki. Auch Shellcheck.net Warnung SC2013
Charles Duffy
3

Es sieht so aus, als würden Sie zweimal lesen. Das Lesen in der while-Schleife wird nicht benötigt. Außerdem müssen Sie den Befehl cat nicht aufrufen:

while read input
do
    echo $input
done < filename
Hai Vu
quelle
4
Das Ziel des OP ist es, dass das Lesen innerhalb der Schleife vom Benutzer kommt, während das äußere darin besteht, aus der Datei zu lesen. Sie wollen also zu Recht zwei verschiedene Lesevorgänge aus zwei verschiedenen Quellen. Dies geht sowohl aus dem Text der Frage (die das readVerhalten des Inneren als "nicht richtig [weil] es versucht, aus der Datei zu lesen filename") als auch aus ihrer akzeptierten Antwort hervor.
Charles Duffy
1

Ich habe diesen Parameter -u mit read gefunden.

"-u 1" bedeutet "von stdin lesen"

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
quelle
-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
quelle