Lire l'entrée utilisateur dans une boucle

99

J'ai un script bash qui est quelque chose comme suivre,

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

mais cela ne me donne clairement pas la bonne sortie car lorsque je lis dans la boucle while, il essaie de lire à partir du nom de fichier du fichier en raison de la redirection d'E / S possible.

Une autre façon de faire de même?

w2lame
la source
La même chose se produit lorsque vous changez d'utilisateur dans bash et exécutez la commande de lecture sous l'utilisateur commuté dans le script
krupal

Réponses:

106

Lire à partir du terminal de contrôle:

read input </dev/tty

plus d'infos: http://compgroups.net/comp.unix.shell/Fixing-stdin-inside-a-redirected-loop

humide
la source
12
-1, car cela contournera toute autre redirection. Par exemple, bash yourscript < /foo/barattendra l'entrée de l'utilisateur, cela n'est acceptable que lors de la lecture des mots de passe. La réponse de @GordonDavisson est préférable pour toutes les autres utilisations.
tiwo
56

Vous pouvez rediriger le stdin normal via l'unité 3 pour le conserver dans le pipeline:

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

BTW, si vous utilisez vraiment de catcette façon, remplacez-le par une redirection et les choses deviennent encore plus faciles:

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

Ou, vous pouvez permuter stdin et unité 3 dans cette version - lisez le fichier avec l'unité 3, et laissez simplement stdin seul:

while read line <&3; do
    # read & use stdin normally inside the loop
    read input
    echo "$input"
done 3<notify-finished
Gordon Davisson
la source
pourquoi votre deuxième script est-il suspendu?
Luca Borrione
2
@LucaBorrione: Comment l'utilisez-vous? Attend-il que vous lui donniez une entrée (notez que la read linelecture à partir de la notification est terminée, mais si vous l'exécutez simplement comme écrit, la read -u 3 inputlecture à partir de la console)?
Gordon Davisson
3

Il semble que vous ayez lu deux fois, la lecture à l'intérieur de la boucle while n'est pas nécessaire. De plus, vous n'avez pas besoin d'appeler la commande cat:

while read input
do
    echo $input
done < filename
Hai Vu
la source
4
Le but de l'OP est que la lecture à l'intérieur de la boucle provienne de l'utilisateur, tandis que la lecture externe est de lire à partir du fichier. Ainsi, ils veulent légitimement deux lectures différentes, de deux sources différentes. Ceci est clair à la fois à partir du texte de la question (décrivant le readcomportement de l'intérieur comme "pas juste [parce que] il essaie de lire à partir du fichier filename") et de leur réponse acceptée.
Charles Duffy
3

Essayez de changer la boucle comme ceci:

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

Test de l'unité:

for line in $(cat /etc/passwd); do
    read input
    echo $input;
    echo "[$line]"
done
dimba
la source
@ w2lame Testé à nouveau, changez la boucle «while» en boucle «for» - cela fonctionne pour moi. Essayez "sex -x" voir d'où vient l'erreur
dimba
4
n'utilisez pas de chat, voir la réponse de Hai Vu
Fredrik Pihl
+1. Cela a été beaucoup plus facile à mettre en œuvre pour mes besoins spécifiques que d'autres suggestions.
Nathan Wallace
1
Voir Ne pas lire les lignes avec pour sur le wiki Wooledge. En outre, shellcheck.net avertissement SC2013
Charles Duffy
1

J'ai trouvé ce paramètre -u avec read.

"-u 1" signifie "lire depuis 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)
xérostome
la source
-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
la source