Marcel Logen <
33320000...@ybtra.de>:
> Helmut Waitzmann in de.comp.os.unix.apps.misc:
>
>> Fragment des Wrapper‐Bash‐Skripts:
>
> Danke. Bei der Analyse Deines Codes bin ich auf ein seltsames
> Verhalten der bash gestoßen:
>
>> exec {fd}< "${tempdir%/}"/ausdruecke.bc
>
> | user14@n14:/tmp$ cat hallo9
> | hallo
>
> | user14@n14:/tmp$ exec {asdf}< hallo9
> | user14@n14:/tmp$ declare -p asdf
> | declare -- asdf="10"
>
> OK, wie zu erwarten.
>
> | user14@n14:/tmp$ unset asdf
> | user14@n14:/tmp$ declare -p asdf
> | bash: declare: asdf: not found
>
> Dann ohne "exec":
>
> | user14@n14:/tmp$ {asdf}< hallo9
>
> Keine (sichtbare) Reaktion der Shell.
>
Da bin ich mir auch nicht sicher, was das bewirkt. Ich bin mir
auch nicht sicher, ob Umlenkungen ohne Kommando überhaupt möglich
sind, und, was sie dann bedeuten. Und weil ich mich im
Allgemeinen – außer, es lässt sich nicht (mit erträglichem
Aufwand) vermeiden – auf den POSIX‐Standard beschränke, kann es
durchaus sein, dass ich irgendwelche Spezialitäten heutiger
Shells nicht kenne.
Aber Christians Vermutung, dass der Shell das als ein externes
Null‐Kommando versteht, würde zum gezeigten Verhalten passen: Es
wird dasselbe ausgeführt wie mit «exec», nur mit dem Unterschied,
dass sich der Shell vorher in einen neuen Prozess forkt. Damit
ist natürlich alles, was in dem neuen Prozess geschieht, nach
dessen Ende im alten nicht vorhanden: weder der File‐Descriptor
noch die Shell‐Variable:
>
> | user14@n14:/tmp$ declare -p asdf
> | bash: declare: asdf: not found
>
> Mir ist nicht klar, was da eigentlich passiert, wenn ich kein
> "exec" angebe.
>
> Mal ein wenig gespielt:
>
> | user14@n14:/tmp$ {asdf;}< hallo9
> | bash: syntax error near unexpected token `}'
Das sieht mir danach aus, dass der Shell hier wegen des
Strichpunkts die Kommandozeile in zwei Teile zerlegt: Der
erste ist das Kommando «{asdf», beendet mit einem Strichpunkt,
und der zweite enthält als erstes die schließende Schweifklammer,
und da fehlt dem Shell die dazu gehörende öffnende.
Probiere im Unterschied dazu mal die folgende Kommandozeile:
{ asdf;}< hallo9
Ob die allerdings funktioniert, bin ich mir auch nicht sicher:
Bedenke, unmittelbar vor die Umleitungsoperatoren darf – ohne
Leerraum dazwischen – (im POSIX‐Standard) eine
File‐Descriptor‐Nummer und (im Bash) alternativ ein Variablenname
in Schweifklammern gesetzt werden.
Allem anderen, was ich vor einen Umlenkungsoperator schreibe, was
aber nichts damit zu tun haben soll, spendiere ich immer einen
Sicherheitsabstand: ein Leerzeichen.
>
> | user14@n14:/tmp$ {asdf ;}< hallo9
> | bash: syntax error near unexpected token `}'
Hier liegt dasselbe wie oben vor: Der Strichpunkt trennt zwei
Kommandos, egal, ob vor ihm Leerraum steht oder nicht.
>
> | user14@n14:/tmp$ {asdf ; }< hallo9
> | bash: syntax error near unexpected token `}'
Das ist ebenfalls das gleiche: Auch nach einem Strichpunkt muss
kein Leerraum stehen, damit er seine Funktion hat.
>
> | user14@n14:/tmp$ { asdf ; }< hallo9
> | bash: asdf: command not found
Ah, hier folgt dieser Fall ja! Der Shell findet die öffnende
Schweifklammer, die nur ihre syntaktische Wirkung für
Kommandolisten hat, wenn an ihr weder vorne noch hinten etwas
dranklebt, was nicht selbsttrennend ist.
> Mit der ksh unter OpenBSD wird ohne "exec" das Terminalfenster
> geschlossen, was für mich auf das erwartete Verhalten hindeutet:
> Der Befehl nach "exec" ersetzt die laufende Shell.
Dass das Terminalfenster geschlossen wird, deutet für mich darauf
hin, dass der in ihm laufende Shell sich beendet. Insofern
verstehe ich jetzt nicht, warum das Terminalfenster gerade ohne
«exec» geschlossen wird. Ich hätte jetzt eher das Gegenteil
erwartet.
> Noch zwei Punkte, die mir auffielen (oder gehören die besser in
> die Ursprungsgruppe dcouam? Grübel):
>
>> {
>> rm -Rf -- "$tempdir"
>> ok &&
> ^^ Wozu ist das nötig?
Das ist nicht nur nicht nötig, sondern sogar falsch. Der Shell
wird sicher einen Syntaxfehler spucken. Du siehst, ich habe das
nicht ausprobiert, ehe ich es abgeschickt habe. Es ist im Eifer
des Gefechts («Wie schreibe ich am besten, damit geschieht, was
ich will?») stehen geblieben.
>> } 3<&-
> ^^^^ Und wozu das? (Der file descriptor 3 wird offenbar
> geschlossen.)
Das ist ebenfalls falsch: Ich hatte das Skript zuerst mit
File‐Descriptor 3 aufgesetzt, mich dann aber entschieden, die
Fähigkeiten des Bashs, sich selber einen File‐Descriptor
auszusuchen und den in einer Variablen bekannt zu machen, zu
nutzen. Es muss richtig also
} {fd}<&-
heißen. Das hat den Vorteil, dass das Skript auch dann noch
funktioniert, wenn du von irgendwoher den File‐Descriptor 3
bereits geöffnet hast und dem «bc» den Parameter «/dev/fd/3»
übergeben willst. Das Skript (und ich als Ersteller) kann ja
nicht wissen, in welcher Umgebung es eingesetzt werden soll.
Diese Möglichkeit, den Shell einen noch freien File‐Descriptor
mittels «{variable}» vor Umlenkungsoperatoren selber aussuchen zu
lassen, sollte unbedingt in den POSIX‐Standard aufgenommen
werden.
Die Umlenkoperation
{fd}<&-
bedeutet, dass der in der Variablen «fd» genannte File‐Descriptor
geschlossen wird. Das ist in diesem Fall vielleicht nicht
unbedingt nötig, weil weder «rm» noch die Shell‐Funktion «ok»
etwas mit ihm anfangen wird.
Aber ich bemühe mich in Shell‐Skripten generell um vorbeugende
File‐Descriptor‐Hygiene: Da, wo ich weiß, dass ich einen
File‐Descriptor, den ich selbst geöffnet habe, nicht brauche,
schließe ich ihn. Das ist wichtig in den Fällen, wo so ein
offengelassener File‐Descriptor von jemandem missbraucht werden
könnte. Stell dir beispielsweise vor, an der Stelle, wo das
«rm»‐Kommando steht, wäre außerdem noch ein Aufruf etwa eines
Texteditors, das Skript also zur interaktiven Verwendung
gedacht. Da könnte man ja dann im Texteditor die Datei
«/dev/fd/3» öffnen, obwohl das nicht im Sinn des
Shell‐Skript‐Erfinders ist.
Das ist übrigens auch der Grund, warum «sudo» in der
Standard‐Konfiguration alle File‐Descriptors (außer 0, 1 und 2)
schließt, aus Sorge davor, dass der «sudo»‐Anwender beim Aufruf
vergessen haben könnte, noch irgendwelche File‐Descriptors, die
er geöffnet hatte, zu schließen, weil der von «sudo» gestartete
Prozess, der unter einer anderen Benutzerkennung läuft, auf
keinen Fall Zugriff aus sie erhalten darf, weil sonst
Sicherheitslücken entstehen.