On Tuesday 23 February 2010 09:04:19 Stefan Plenert wrote:
http://openbook.galileocomputing.de/linux/linux_03_shell_010.htm#mj80ead4a3 7a265d23a9fc4199cd6c2447
3.10 Subshells und Kommandogruppen
Prozessgruppe - :~> { ls ; uname } | tee log.txt - > - :~> { ls; uname } | tee log.txt - >
- :~> Katze=Felix - :~> ( Katze=Mausi; echo $Katze ) - Mausi - :~> echo $Katze - Felix - :~> { Katze=Minka; echo $Katze } - >
:~> bash --version
GNU bash, version 3.2.39(1)-release (i586-suse-linux-gnu) Copyright (C) 2007 Free Software Foundation, Inc.
Nach { } bleibt bei mir die nächste Zeile nach > leer Auch wenn ich das Beispiele aus openbook in die Konsole Kopiere.
Was mache ich falsch?
Du solltest Dir ein anderes Buch zulegen. So oft, wie Du hier schon fragen zu dem Ding gestellt hast, hat der Autor es nicht verdient, daß man ihn liest. Jetzt zum Thema, die Shell braucht ein Semikolon (;), einen Ampersand (&) oder einen Zeilenumbruch, um das Ende des Kommandos zu erkennen. Zur Verdeutlichung: $ { Katze=Minka; echo $Katze } ; } Minka } $ { Katze=Minka; echo $Katze; } Minka $ { Katze=Minka; echo $Katze& } [1] 4086 Minka $ {
Katze=Minka echo $Katze } Minka
Die '> ' Zeichen am Zeilenanfang im letzten Beispiel kommen aus der PS2- Variablen. Sie gehören nicht zur Eingabe. Im vorletzten Beispiel wird "echo $Katze" als Hintergrundprozess gestartet, daher die Ausgabe "[1] 4086". Das Beispiel aus dem Buch "{ ls; uname; } | tee log.txt" ist übrigens auch leicht irreführend (ich habe mir erlaubt, das Semikolon nach uname einzufügen). In der Beschreibung steht was von Prozessgruppen im Unterschied zur Subshell. Wenn Du die geschweiften Klammern aber mit einer Pipe als Aus- oder Eingabeumleitung verbindest, startet die Bash trotzdem eine Subshell. $ x=hugo; echo $x; { x=klaus; echo $x; }; echo $x; hugo klaus klaus Das ist sicher das, was Du erwartest. x=klaus in der Klammer setzt die globale Variable x. Doch was passiert hier? $ { x=fritz; echo $x; } | cat; echo $x fritz klaus Das sieht dem Verhalten der Subshell doch sehr ähnlich: $ ( x=fritz; echo $x; ); echo $x fritz klaus Übrigens lügt die Shell bzgl. ihres Pids in Subshells: $ ( x=fritz; echo $$; ); echo $$ 4109 4109 Hier müssten doch unterschiedliche Zahlen stehen, oder? (Zur Notwendigkeit von x=fritz kommen wir später.) $ { echo $$; }; echo $$ 4109 4109 Das hier ist richtig, da es keine Subshell gibt. $ { echo $$; } | cat; echo $$ 4109 4109 Hier müssten wieder unterschiedliche Zahlen stehen. Den nächsten Fall hatten wir noch nicht, eine Kommandogruppe mit Ausgabeumleitung, wobei nicht in eine Pipe, sondern eine Datei umgeleitet wird. Ich benutze /dev/tty, damit die Ausgabe auf dem Bildschirm erscheint. Jede andere Datei würde es auch tun. $ { echo $$; } >/dev/tty; echo $$ 4109 4109 Diese Zahlen sind richtig, da keine Subshell erzeugt wird. Wie komme ich darauf, daß die Bash in den verschiedenen Fällen lügt? Dazu brauchen wir eine andere, Bash-unabhängige Methode den Pid zu ermitteln. Schau Dir mal /proc/self/status an: $ cat /proc/self/status Name: cat State: R (running) Tgid: 4290 Pid: 4290 PPid: 4109 ... Die Pid-Zeile zeigt den Pid des cat-Befehls, PPid jedoch den Pid der Shell. Gut, nun können wir die obigen Beispiele wiederholen und PPid aus /proc/self/status benutzen. Eine einfache Kommandogruppe - keine Subshell: $ { grep PPid /proc/self/status; }; grep PPid /proc/self/status PPid: 4109 PPid: 4109 Kommandogruppe mit Ausgabeumleitung in Datei - keine Subshell: $ { grep PPid /proc/self/status; } >/dev/tty; grep PPid /proc/self/status PPid: 4109 PPid: 4109 Kommandogruppe mit Ausgabeumleitung in Pipe - Subshell: $ { grep PPid /proc/self/status; } | cat; grep PPid /proc/self/status PPid: 4313 PPid: 4109 Subshell: $ ( grep PPid /proc/self/status; ); grep PPid /proc/self/status PPid: 4109 PPid: 4109 Oops, das verwundert doch ein wenig. Ich habe ihm gesagt, er soll eine Subshell machen, er macht es aber nicht. Hier optimiert die Shell offensichtlich. Gut, fügen wir in die Klammer ein weiteres Kommando ein, das eigentlich nichts machen sollte (vgl. "( x=fritz; echo $$; ); echo $$" weiter oben): $ ( x=hugo; grep PPid /proc/self/status; ); grep PPid /proc/self/status PPid: 4329 PPid: 4109 Jetzt gibt es wirklich eine Subshell. Das Wundern geht noch ein bisschen weiter. Nicht mal eine Ausgabeumleitung in eine Pipe erzwingt im vorletzten Beispiel die Subshell. Der Optimierer schlägt offensichtlich relativ früh zu: $ ( grep PPid /proc/self/status; ) | cat; grep PPid /proc/self/status PPid: 4109 PPid: 4109 Erst ein zweites Kommando erzeugt wirklich eine Subshell: $ ( echo klaus; grep PPid /proc/self/status; ) | cat; grep PPid /proc/self/status klaus PPid: 4352 PPid: 4109 Nun noch ein Beispiel mit einer Shell-Funktion: $ pid () { grep PPid /proc/self/status; } $ pid PPid: 4109 Die pid-Funktion ist nur eine Kapselung des grep-Befehls. $ ( pid ) | cat; pid PPid: 4404 PPid: 4109 Huch, wieso optimiert er das nicht? Naja, wenigstens bleibt er konsistent: $ ( pid ); pid PPid: 4417 PPid: 4109 Mit Kommandogruppen geht es immer noch: Keine Subshell: $ { pid; }; pid PPid: 4109 PPid: 4109 $ { pid; } >/dev/tty; pid PPid: 4109 PPid: 4109 Ausgabeumleitung in Pipe erzeugt Subshell: $ { pid; } | cat; pid PPid: 4438 PPid: 4109 Außer Funktionen gibt es in der Shell noch Aliase, um Kommandos abzukürzen. Du kannst die obige Reihe nochmal durchführen nachdem Du mit alias pid='grep PPid /proc/self/status' eine Abkürzung definiert hast. Ich glaube, Du wirst das Verhalten wie bei dem ausgeschriebenen grep-Kommando finden. Sicher bin ich mir da aber nicht. Verwirrend, oder? Die Autoren hätten wirklich besser recherchieren sollen. Torsten -- Um die Liste abzubestellen, schicken Sie eine Mail an: opensuse-de+unsubscribe@opensuse.org Um eine Liste aller verfuegbaren Kommandos zu bekommen, schicken Sie eine Mail an: opensuse-de+help@opensuse.org