Wie lese ich Benutzereingaben aus einer Pipe?

9

Nehmen wir an, ich habe eine Datei confirmation.shmit dem folgenden Inhalt:

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

und ich möchte dieses Skript folgendermaßen ausführen:

cat confirmation.sh | sh

Ich sehe Are you sure [Y/n]?und das Skript wird unterbrochen. Was ist das Problem?

Igor Timoshenko
quelle
2
Sie haben /bin/bashin der Bang-Zeile, aber Sie verwenden eine .shErweiterung und versuchen, das Skript zu leiten sh. Kein Problem, da der Code, den Sie haben, mit beiden kompatibel ist, aber es lohnt sich darauf hinzuweisen.
Graeme

Antworten:

8

Wie andere gesagt haben, liegt dies daran, dass das stdinvon shzum Lesen aus der Pipe umgeleitet wurde und nicht wie gewohnt mit dem Terminal verbunden ist. Eine Möglichkeit /dev/tty, dies zu umgehen , besteht darin, das Lesen des Skripts vom Terminal zu erzwingen. Z.B:

#!/bin/sh

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

Normalerweise würden Sie dies nur tun, wenn Sie speziell verhindern möchten, dass Personen die Eingabe als Skript ausführen, z.

echo Y | sh confirmation.sh

Dies würde immer noch vom Terminal gelesen, obwohl der Benutzer möglicherweise erwartet, dass dies automatisch Yan der Eingabeaufforderung eingegeben wird . Es ist üblich, dass Programme, die ein Kennwort erwarten, dies tun.

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

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

Hinweis: Vielen Dank an @Graeme für die Korrektur der beiden oben genannten Beispiele ...

Es ist viel einfacher, wenn Sie sich stdinklar halten .

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

Oder, da die 0 1 2 eines Terminals alle dieselbe Datei sind, fügen Sie einfach Folgendes hinzu:

read line <&2

Und dein

cat ./confirmation.sh | sh

Funktioniert gut.

mikeserv
quelle
Ich kann nichts davon außer dem letzten zum Laufen bringen.
Graeme
Seltsam, sie haben alle für mich gearbeitet ... Ich bin mitten in etwas, aber in ... 10 oder so Minuten kann ich es erneut testen. In der Zwischenzeit ... BOLD DEIN KOMMENTAR vielleicht? Ich mag es nicht, Fehlinformationen zu verbreiten.
Mikeserv
@Graeme - nicht sicher, warum ich es das erste Mal verpasst habe - hätte schwören können, dass ich sie alle gleichzeitig getestet habe - aber die ersten beiden brauchen eine Änderung, um richtig zu funktionieren. Danke.
Mikeserv
Sieht gut aus, es liest aber immer noch nicht vom Terminal, wenn ich es stderrbeschaffe. Ich sollte es wahrscheinlich tun, ich verstehe nicht warum.
Graeme
@ Graeme - komisch - ich habe es mit dash sh zsh und bash versucht. Alles hat funktioniert.
Mikeserv
0

Dies hat für ein einzelnes Argument funktioniert, das an mein Skript weitergeleitet wurde:

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

Ich kann dann in einem Positionsargument ( $1) auf die weitergeleiteten Daten zugreifen , das mit meinen anderen Skriptfunktionen kompatibel ist.


Von info test:

[ -p /dev/stdin ]: -p FILEist True, wenn FILE existiert und eine Named Pipe ist.

LinuxSecurityFreak
quelle
-1

Die kurze Antwort lautet: Sie können nicht. Die Pipe leitet stdout nach stdin um, sodass Sie kein interaktives Skript ausführen können, da Sie die Ausgabe des ersten Befehls bereits als Eingabe zum zweiten Befehl in der Pipe-Anweisung umgeleitet haben.

Vielleicht möchten Sie so etwas tun:

cat confirmation.sh > ask.sh && sh ask.sh
David
quelle
Sie können weiterhin ein interaktives Skript ausführen, siehe meine Antwort. Warum auch nicht einfach sh confirmation.sh?
Graeme