Comment lire les entrées utilisateur d'un tuyau?

9

Supposons que j'ai un fichier nommé confirmation.shavec le contenu suivant:

#!/bin/bash
echo -n "Are you sure [Y/n]? "
read line
case "$line" in
    n|N) echo "smth"
        ;;
    y|Y) echo "smth"
        ;;
esac

et je veux exécuter ce script de la manière suivante:

cat confirmation.sh | sh

Je vois Are you sure [Y/n]?et le script est interrompu. Quel est le problème?

Igor Timoshenko
la source
2
Vous avez /bin/bashdans la ligne de coup, mais vous utilisez une .shextension et essayez de diriger le script vers sh. Ce n'est pas un problème car le code que vous avez est compatible avec les deux, mais mérite d'être souligné.
Graeme

Réponses:

8

Comme d'autres l'ont dit, c'est parce que le stdinof sha été redirigé pour lire à partir du tuyau, il n'est pas connecté au terminal comme il le serait normalement. Une chose que vous pouvez faire pour contourner ce problème est d'utiliser /dev/ttypour forcer le script à lire depuis le terminal. Par exemple:

#!/bin/sh

read -p "Are you sure [Y/n]?" line </dev/tty
case "$line" in
  y|Y) echo "confirmed"
    ;;
  *) echo "not confirmed"
    ;;
esac

Normalement, vous ne feriez cela que si vous voulez spécifiquement empêcher les gens de créer un script pour l'entrée, par exemple:

echo Y | sh confirmation.sh

Cela continuerait à être lu à partir du terminal même si l'utilisateur pouvait s'attendre à ce que cela soit automatiquement entré Yà l'invite. Il est courant pour les programmes qui attendent un mot de passe de le faire.

Graeme
la source
2
sh 3<<CONFIRM /dev/fd/3
    $(cat ./confirmation.sh)
CONFIRM

sh 3<./confirmation.sh /dev/fd/3

note: merci à @Graeme de m'avoir corrigé sur les deux exemples ci-dessus ...

C'est beaucoup plus facile à faire si vous restez à l' stdinécart.

2<./confirmation.sh . /dev/stderr

Ou, puisque les 0 1 2 d'un terminal sont tous le même fichier, ajoutez simplement:

read line <&2

Et ton

cat ./confirmation.sh | sh

Fonctionne très bien.

mikeserv
la source
Je ne peux pas faire fonctionner ces éléments en dehors du dernier.
Graeme
Bizarre, ils ont tous travaillé pour moi ... Je suis au milieu de quelque chose, mais dans ... environ 10 minutes, je peux tester à nouveau. En attendant ... GRAS VOTRE COMMENTAIRE peut-être? Je n'aime pas diffuser de fausses informations.
mikeserv
@Graeme - je ne sais pas pourquoi je l'ai raté la première fois - aurait juré que je les ai tous testés en même temps - mais les deux premiers ont besoin d'un changement pour fonctionner correctement. Je vous remercie.
mikeserv
Ça a l'air bien, il ne lit toujours pas depuis le terminal quand je source stderr. Je devrais probablement cependant, je ne comprends pas pourquoi.
Graeme
@Graeme - bizarre - je l'ai essayé avec dash sh zsh et bash. Tout fonctionnait.
mikeserv
0

Cela a fonctionné pour un seul argument dirigé vers mon script:

if [ -p /dev/stdin ]; then set -- "$( cat )"; fi

Je peux ensuite accéder aux données canalisées dans un argument positionnel ( $1), qui est compatible avec mes autres fonctionnements de script.


De info test:

[ -p /dev/stdin ]: -p FILEvaut True si FILE existe et est un canal nommé.

LinuxSecurityFreak
la source
-1

La réponse courte est que vous ne pouvez pas. Le canal redirige stdout vers stdin, vous ne pouvez donc pas exécuter un script interactif, car vous avez déjà redirigé la sortie de la première commande comme entrée vers la seconde commande dans l'instruction pipe.

Vous cherchez peut-être à faire quelque chose comme ceci:

cat confirmation.sh > ask.sh && sh ask.sh
David
la source
Vous pouvez toujours exécuter un script interactif, voir ma réponse. Et pourquoi pas seulement sh confirmation.sh?
Graeme