So stellen Sie eine Variable aus einer Subshell in der übergeordneten Shell zur Verfügung

10

Ich habe ein schnelles und schmutziges Skript geschrieben, um einige Berichte von einem Webdienst zeitlich abzustimmen:

BASE_URL='http://example.com/json/webservice/'
FIRST=1
FINAL=10000

for report_code in $(seq 1 $FINAL); do
  (time -p response=$(curl --write-out %{http_code} --silent -O ${BASE_URL}/${report_code}) ) 2> ${report_code}.time

  echo $response  # <------- this is out of scope!  How do I fix that?
  if [[ $response = '404' ]]; then
    echo "Deleting report # ${report_code}!"
    rm ${report_code}
  else
    echo "${report_code} seems to be good!"
  fi
done

Ich muss den timeBefehl in eine Subshell einschließen, damit ich seine Ausgabe umleiten kann, aber das macht den Wert von $responsefür die übergeordnete Shell nicht verfügbar. Wie komme ich um dieses Problem herum?

Bilderstürmer
quelle

Antworten:

11

Sie können den Wert einer Variablen nicht von einer Subshell zu ihrem übergeordneten Element bringen, nicht ohne fehleranfälliges Marshalling und umständliche Kommunikation.

Zum Glück brauchen Sie hier keine Subshell. Die Umleitung erfordert nur eine Befehlsgruppierung mit { … }, keine Unterschale.

{ time -p response=$(curl --write-out '%{http_code}' --silent -O "${BASE_URL}/${report_code}"); } 2> "${report_code}.time"

(Vergessen Sie nicht doppelte Anführungszeichen um Variablensubstitutionen .)

Gilles 'SO - hör auf böse zu sein'
quelle
Es ist lustig, dass jede Programmiersprache das kann. Ich habe bereits 1 Stunde gegoogelt, ohne eine mögliche Lösung in der Shell. Ich bin enttäuscht.
Zu Kra
1
@ToKra Nein. Keine Programmiersprache kann dies. (Fast) jede Programmiersprache kann den Wert einer Variablen von einer Unterroutine oder einem Befehlsblock zu ihrem übergeordneten { … }Element bringen , und genau das erkläre ich in meiner Antwort: Verwenden Sie die Befehlsgruppierung anstelle einer Unterschale ( … ).
Gilles 'SO - hör auf böse zu sein'
4

Mitbenutzer von U & L: Bevor Sie meine Antwort für die Verwendung von C-Stil mit main()Funktion ablehnen , besuchen Sie bitte diesen Link: /unix//a/313561/85039 Die Verwendung von Hauptfunktionen in Skripten ist eine gängige Praxis, die von vielen Fachleuten verwendet wird im Feld.


Wie Gilles betonte, können Subshells keine Variablen außerhalb ihrer Umgebung verfügbar machen. Aber lassen Sie uns dieses Problem aus einem anderen Blickwinkel betrachten: Wenn Sie Ihr Skript in Funktionen schreiben, können Sie Variablen als deklarieren localund diese können bearbeitet werden.

Aus dem Handbuch von Bash 4.3, localBeschreibung:

... Wenn local innerhalb einer Funktion verwendet wird, hat der Variablenname einen sichtbaren Bereich, der auf diese Funktion und ihre untergeordneten Funktionen beschränkt ist ...

Beispiel:

#!/bin/bash

outter()
{
    for i in $(seq 1 3)
    do
        var=$i
    done
}

main()
{
    local var=0
    outter
    echo $var
}
main "$@"
$ ./testscript.sh                                                                                                        
3

Wie Sie nach 3 Iterationen der Schleifenfunktion sehen können, wird die Variable geändert.

Sergiy Kolodyazhnyy
quelle
Hmm, unintuitives Verhalten für C-Programmierer. Normalerweise würde ich erwarten outter, auf eine globale Instanz von zu verweisen var.
Ruslan
Diese Antwort hat einige Mängel. "$var"wird beim Aufruf nicht benötigt outter. Es macht nichts. Und local var=0macht auch nichts; Der Anruf outterwird überschrieben var, wie Sie angegeben haben.
Golar Ramblar
@GolarRamblar Ich habe "$var"als Positionsargument entfernt outter; um fair zu sein, ist dies eine Gewohnheit. Können Sie etwas näher erläutern local var=0 ?
Sergiy Kolodyazhnyy