Am 02.08.22 um 07:15 schrieb tomas@tuxteam.de:
On Mon, Aug 01, 2022 at 05:17:25PM +0200, Arno wrote:
Beim booten läuft bei mir ein script. Gestartet aus einem rc5.d/Sxx. [...] Soweit so normal.
Jetzt ist mein script schon etwas länglich geworden und es droht die Übersicht verloren zu gehen. Also hab ich es in Funktionen aufgeteilt. Damit wird das script aber nicht kürzer. Das ganze möchte ich etwas wartungsfreundlicher und lesbarer gestalten. Ideal wäre ein einem Startscript die Variablen/Pfade zu setzen und dann bestimmte Unterscripts zu starten. Was aber dabei ein Problem zu sein scheint: a) die Variablen vom Startscript übernehmen b) Variablen im Idealfall auch noch von Unterscript zu Unterscript weiterreichen
Die Frage ist ziemlich allgemein gestellt, deshalb kann nur eine ziemlich allgemeine Antwort gegeben werden.
Was an der Shell besonders ist (und heute eher ungewöhnlich, und ungewohnt) ist, dass die "Variablen" einen dynamischen Scope haben, d.h., wenn ich eine in einem Stück Shell setze, dann ist sie in allen Stücken, die von dort aus aufgerufen werden auch "sichtbar" (wenn das Aufrufen nicht als (shell-) Funktion passiert, sondern durch "Aurufen" einer anderen Datai (Subshell), dann muss die Variable EXPORTiert werden).
Eine andere Ungewöhnlichkeit ist das Textersetzungsmodell: eine Zeile wird erst nach bestimmten Regeln transformiert ($variable wird durch ihr Wert ersetzt, $(ausdruck) auch, etc.) und dann erst interpretiert.
Diese zwei Dinge zu managen führt zu einem etwas anderen Stil in der Programm"architektur".
So. Genug herumgeschwafelt ;-)
Erschwerend kommt hinzu, dass das ganze während des Bootens passiert, also nicht aus einer klassischen shell heraus. Ich meine, das nicht mal a) da so ohne weiteres geht. Oder gibt es da Tricks? Ich hab nicht so viel sinnvolles dazu gefunden oder ich hab nicht die richtigen keywords dazu.
Das ist alles halb so wild. Shell ist shell. Du musst wissen, mit welcher Du es zu tun hast (ist es die bash? ist es ihre kleine Schwester) und
Ja, es ist nur die /bin/sh
Du musst berücksichtigen, mit welcher Umgebung Du es zu tun hast (ist $PATH so wie Du sie erwartest? Musst Du ggf. "/bin/rm" statt nur "rm" sagen? Plane etwas Zeit ein fürs Debuggen).
Das ist klar, da verwende ich nur absolute Pfade.
Zusatzfrage: Wenn ein Script wie meines gerade aus Funktionen besteht: Am Ende der Funktionsdefinitionen werden die aufgerufen. Kann man z.B. zum Testen, diese Funktionen (aus einer echten) Shell auch einzeln aufrufen?
How do the masters do it?
Einen netten Ansatz findest Du bei den alten sysvinit-Skripten. Die ersten zwei Zeilen (abgesehen von Kommentaren) in einem (Quizfrage: welches?) von meinen sehen so aus:
[ -r /usr/share/postgresql-common/init.d-functions ] || exit 0 . /usr/share/postgresql-common/init.d-functions
Es wird eine Datei "gesourct", in der irgendwelche nützliche Funktionen definiert werden. Das würde ich mal das "library"-Pattern nennen.
Konkret: Wenn value=10 vor dem Aufruf von init.d-functions steht, dann ist value dort bekannt. Aber was wenn ich in init.d-functions value2=20 setze. Kann ich die 20 in dem aufrufenden Vater-Script verwenden?
Also nur einzelne Funktionen um das Step by Step zu testen. Wie gesagt, die Pfade/Variablen müssen gesetzt bleiben, sonst ist es sinnlos.
Was auch immer Du damit meinst. Das muss konkreter werden. Wenn ich eine Variable in einer Funktion setze, dann ist dieser Zustand im die Funktion einschliessenden Skript sichtbar. Nicht jedoch, wenn ich sie in einem "Unterskript" setze.
Bei läuft dann immer nur das ganze Script... Wenn ich das richtig verstanden hab, kann man nur dann einzelne Funktionen eines scripts aufrufen, wenn es kein "main" gibt, also sozusagen nur Funktionen. Ist das wahr?
Ich weiss nicht genau, wie ich die Aussage im Kontext einer Shell verstehen soll. Vermutlich nicht. Wer oder was ist "main"?
Nehmen wir an, ich habe ein Skript, nennen wir es "muuh":
#!/bin/bash echo "Guten Morgen"
say-hello () { echo hello }
say-good-bye () { echo good bye }
Und dann "source" ich das Ding in einem anderen:
... . muuh ...
Ok aber kann ich auch (Syntax frei erfunden) . muh::say-hello() machen und dann wird nur "hello" zurückgebeben. Das meine ich, wenn ich die Funktionen eines Scripts testen will.
Bei mir wäre gegenwärtig das Problem das das muh wohl so aussieht:
#/bin/sh funk1() { echo 1 } funk2() { echo 2 } funk3() { echo 3 } echo Test funk1 echo blabla funk3 funk2
Ich müsste zum Test die unteren 5 Zeilen komplett rausnehmen und source muh.sh funk2
machen um nur funk2 zu testen, richtig?
Was passiert ist, dass wenn die Ausführung in meinem Skript an diesen Punkt kommt, "Guten Morgen" ausgegeben wird (wohin immer gerade stdout zeigt) und die zwei Funktionen definiert werden, die ich dann im weiteren Verlauf verwenden kann.
Immerhin. Also sind die Funktionen auch nach Ausführung noch verfügbar.
Ein ganz gerader, naiver Interpreter: mach' dies, dann mach' das. Ob die Ausgabe von "Guten Morgen" jetzt erwünscht war oder nicht, das kannst nur Du wissen. Typischerweise wird man so eine Library so bauen, dass sie nur Funktionen definiert, sei es nur darum, nicht den Verstand zu verlieren. Vielleicht meintest Du das mit "main".
Solche allgemeinen Regeln finde ich immer suspekt.
Da gibt es noch ein anderes Muster, das mit den Subcommands (inspiriert durch git). Aber das möge erst mal genügen :)
lg
Also vielen Dank für die Mühe, dann werd ich mich mal ans refactoring machen und mit kleinen Demos beginnen.
Viele Grüße, Arno