Der beste Weg, um Variablen in einem Quell-Bash-Skript lokal zu machen?

7

Ich habe ein Bash-Skript, das einen Bericht über den Fortschritt einiger lang laufender Jobs auf dem Computer generiert. Grundsätzlich durchläuft dieses übergeordnete Skript eine Liste von untergeordneten Skripten (wobei alle aufgerufen werden source). Von den untergeordneten Skripten wird erwartet, dass sie einige spezifische Variablen festlegen, die das übergeordnete Skript dann verwendet.

Heute habe ich einen Fehler entdeckt, bei dem eine vom ersten untergeordneten Skript festgelegte Variable versehentlich vom zweiten untergeordneten Skript verwendet wurde und eine falsche Ausgabe verursachte. Gibt es eine saubere Möglichkeit, um das Auftreten solcher Fehler zu verhindern?

Wenn ich sourceein untergeordnetes Skript habe, gibt es grundsätzlich einige spezifische Variablen, die ich für das übergeordnete Skript beibehalten möchte. Mein übergeordnetes Skript setzt diese spezifischen Variablen vor sourcejedem neuen untergeordneten Skript zurück, sodass keine Probleme mit ihnen auftreten. Einige untergeordnete Skripte können jedoch zusätzliche beliebige Variablen enthalten, die lokal verwendet werden und die nicht im übergeordneten Skript verbleiben sollten.

Natürlich könnte ich jedes dieser Elemente am Ende des untergeordneten Skripts manuell deaktivieren, aber diese scheinen fehleranfällig zu sein, wenn ich eines vergesse. Gibt es eine geeignetere Möglichkeit, ein Skript zu beschaffen und nur bestimmte Variablen für das aufgerufene Skript beizubehalten source?

edit: Aus Gründen der Klarheit ist hier eine Art heruntergekommene Version meines übergeordneten Skripts:

echo "<html><body><h1>My jobs</h1>"

FILES=~/confs/*.sh
for f in $FILES; do
  # reset variables
  name="Unnamed job"
  secsSinceActive="Unknown"
  statusText="Unknown"

  # run the script that checks on the job
  source "$f"

  # print bit of report
  echo "<h3>$name</h3>"
  echo "<p>Last active: $secsSinceActive seconds ago</p>"
  echo "<p>Status: $statusText</p>"

echo "</body></html>"

Und so könnte eines der untergeordneten Skripte aussehen:

name="My awesome job"

nowTime=`expr $(date +%s) `
lastActiveTime=`expr $(date +%s -r ~/blah.log)`
secsSinceActive=`expr $nowTime - $lastActiveTime`

currentRaw=$(cat ~/blah.log | grep "Progress" | tail -n 1)
if [ -z "$currentRaw" ]; then
  statusText="Not running"
else
  statusText="In progress"
fi

Die Variablen $ name, $ secsSinceActive und $ statusText müssen im übergeordneten Skript verbleiben, aber alle anderen Variablen sollten verschwinden, wenn das untergeordnete Skript beendet wird.

Hayden Schiff
quelle
Könnten Sie die sourceTeile einer Unterschale aufrufen und überprüfen, damit sie die Eltern nicht dauerhaft beeinträchtigen?
Eric Renouf
Wenn ich das täte, wie konnte ich die spezifischen Variablen halten , dass ich sie vom Kind zum Vater zu bestehen bleiben wollen?
Hayden Schiff
@ Oxyguy3 Wenn Sie sie deaktivieren, dachte ich, Sie brauchten sie nicht wirklich, um hartnäckig zu sein, vielleicht gibt es aber noch andere
Eric Renouf
@EricRenouf Ich habe aus Gründen der Übersichtlichkeit einen Beispielcode hinzugefügt. Ich brauche einige spezifische Variablen, um zu bestehen, aber jede Variable außerhalb meiner Liste spezifisch genehmigter Variablen muss sterben.
Hayden Schiff
2
Dies ist die Quintessenz der Verwendung globaler Variablen bei der Programmierung. Sie sollten hierfür einen robusteren Mechanismus verwenden, z. B. das Parsen von Werten aus der Ausgabe jedes Unterprozesses oder das Speichern von temporären Dateien.
Goldlöckchen

Antworten:

9

Wickeln Sie das gesamte Skript, das Sie als Quelle verwenden möchten, in eine Funktion ein, fügen Sie vor den Deklarationen, die Sie nur in der Funktion verwenden möchten, local hinzu und rufen Sie die Funktion am Ende des Skripts auf.

func () {
    local name="My awesome job"

    nowTime=`expr $(date +%s) `
    lastActiveTime=`expr $(date +%s -r ~/blah.log)`
    local secsSinceActive=`expr $nowTime - $lastActiveTime`

    currentRaw=$(cat ~/blah.log | grep "Progress" | tail -n 1)
    if [ -z "$currentRaw" ]; then
      local statusText="Not running"
    else
      local statusText="In progress"
    fi
}
func
MichalH
quelle
1
Die beste Lösung imo, aber für diejenigen, die diese Antwort lesen, denken Sie daran, dass die obige func()Funktion jedoch im übergeordneten Bereich vorhanden bleibt und sie verschmutzt; Um widersprüchliche Namen zu vermeiden, ist die beste Lösung, die ich kenne, ihm einen hässlichen Namen zu geben, so etwas wie___this_main___()
Yolenoyer
1
Um die Funktion selbst zu bereinigen, können Sie sie unset -f funcdirekt nach dem Aufruf verwenden.
JWD
-1

Ich glaube, die Syntax zum Erstellen einer lokalen Variablen in bash lautet:

local variable_name=

Ich weiß, dass dies für Funktionen funktioniert und bin mir nicht sicher, wie es mit mehreren Skripten funktioniert.

DannyK
quelle
1
Es scheint, dass es nur für Funktionen verwendet werden kann; Ich erhalte eine Fehlermeldung:local: can only be used in a function
Hayden Schiff