Am 02.08.22 um 07:15 schrieb tomas(a)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