2016-06-30 12 views
2

Nehmen wir an, ich habe ein main.sh Skript, das über eine Subshell one.sh aufrufen wird.Bash Subshell konsumiert Stdin des übergeordneten Prozesses

one.sh:

#! /bin/bash 

set -euo pipefail 

(if [ -t 0 ]; then 
    echo "one little two little three little buses" 
else 
    cat - 
fi) | awk '{ $1 = "111"; print $0 }' 

main.sh:

#! /bin/bash             

set -euo pipefail           

main() {              
    echo "one_result) $(./one.sh)"        

    echo "one_piped) $(echo "the quick brown fox" | ./one.sh)" 
}               

main               

nun jeder von ihnen wie erwartet funktioniert:

$ ./one.sh 
111 little two little three little buses 

$ ./main.sh 
one_result) 111 little two little three little buses 
one_piped) 111 quick brown fox 

Aber, wenn ich Rohr etwas zu main.sh, war ich nicht erwarten (oder besser gesagt, ich will nicht) one.sh über den verrohrt Inhalt wissen, weil ich dachte, one.sh war in seiner eigenen Subshell in one_result):

$ echo "HELLO WORLD MAIN" | ./main.sh                     
one_result) 111 WORLD MAIN 
one_piped) 111 quick brown fox 

Ist es der Fall, mein if Zustand in one.sh ist nicht das, was ich will? Ich möchte one.sh keine Nebenwirkungen von der Verwendung meiner main.sh 's stdin erstellen - seit jetzt hat es verbraucht, meine main.sh ist jetzt effektiv stdin -less, wie Stdin kann nur einmal gelesen werden, es sei denn ich es weg speichern.

Gedanken? TIA.

Antwort

3

Im Allgemeinen erben Subshells (und andere Prozesse, die eine Shell hervorbringt) stdin von der übergeordneten Shell. Wenn das das Terminal ist, wird Ihr Test wie erwartet funktionieren; Wenn es eine Pfeife ist, wird es erkennen, dass es eine Pfeife ist, und weitermachen und es verbrauchen. Es gibt keine Möglichkeit für die Subshell zu sagen, ob sie diese Pipe erhalten hat, indem sie explizit zugewiesen wurde (wie in echo "the quick brown fox" | ./one.sh) oder über Vererbung. Das einzige, was ich sehen kann, ist das explizite Weiterleiten der one.sh Eingabe an etwas anderes als eine Pipe, um zu vermeiden, dass sie die stdin der Eltern-Shell erbt (was eine Pipe sein könnte). Etwas wie:

echo "one_nonpipe) $(./one.sh </dev/null)"        

echo "one_piped) $(echo "the quick brown fox" | ./one.sh)" 

... aber was hat noch besser wäre ein Flag hinzuzufügen one.sh zu sagen, ob von stdin zu lesen oder nicht, anstatt zu versuchen, es aus der Art der Datei, um herauszufinden, befestigt zu stdin:

#! /bin/bash 
# Usage: one.sh [-i] 
# -i Read from stdin 

set -euo pipefail 

if [ "$1" = "-i" ]; then 
    cat - 
else 
    echo "one little two little three little buses" 
fi | awk '{ $1 = "111"; print $0 }' 

... 

echo "one_result) $(./one.sh)"        

echo "one_piped) $(echo "the quick brown fox" | ./one.sh -i)" 

(Beachten sie, dass ich die unnötigen Klammern um den if Block auch entfernt - ein andere Ebene der Sub-Shell für keinen guten Grund, warum sie erstellt.)

2

Standardmäßig erbt jeder Prozess seine Standardeingabe (und Ausgabe und Fehler) von seinem übergeordneten Element. Eingabeumleitung und Pipes sind zwei Möglichkeiten, die Standardeingabe in einen anderen Dateideskriptor zu ändern, wenn der untergeordnete Prozess gestartet wird.

Es liegt in der Verantwortung von main.sh einem, wenn von der Standardeingabe zu lesen, um zu wissen, dass one.shauch von der Standardeingabe liest, und es wird one.sh aus dem Konsum verhindern müssen.

+1

Eine mögliche Abhilfe: set 'one.sh' 'STDIN zu'/dev/null ', so dass es nicht von "stehlen" kann Die Eingabe des Elternteils. Das Ergebnis würde folgendermaßen aussehen: $ (one.sh