Comment rendre une variable d'un sous-shell disponible dans le shell parent

11

J'ai écrit un script rapide et sale pour chronométrer certains rapports d'un service Web:

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

J'ai besoin d'envelopper la timecommande dans un sous-shell afin que je puisse rediriger sa sortie, mais cela rend la valeur de $responseindisponible pour le shell parent. Comment contourner ce problème?

iconoclaste
la source

Réponses:

11

Vous ne pouvez pas apporter la valeur d'une variable d'un sous-shell à son parent, non sans effectuer un marsling sujet aux erreurs et une communication encombrante.

Heureusement, vous n'avez pas besoin d'un sous-shell ici. La redirection nécessite uniquement un regroupement de commandes avec { … }, pas un sous-shell.

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

(N'oubliez pas les guillemets doubles autour des substitutions de variables .)

Gilles 'SO- arrête d'être méchant'
la source
c'est drôle que chaque langage de programmation puisse le faire. j'ai déjà passé 1h à googler sans solution possible en shell. Je suis déçu.
À Kra
1
@ToKra Non. Aucun langage de programmation ne peut le faire. (Presque) n'importe quel langage de programmation peut apporter la valeur d'une variable d'un sous - programme ou d'un bloc d'instructions à son parent, et c'est précisément ce que j'explique dans ma réponse: utiliser le groupement de commandes { … }au lieu d'un sous-shell ( … ).
Gilles 'SO- arrête d'être méchant'
4

Chers utilisateurs U&L: Avant de voter contre ma réponse pour l'utilisation du style C avec la main()fonction, veuillez visiter ce lien: https://unix.stackexchange.com/a/313561/85039 L' utilisation des fonctions principales dans les scripts est une pratique courante, utilisée par de nombreux professionnels Sur le terrain.


Comme l'a souligné Gilles, les sous-coquilles ne peuvent pas rendre les variables disponibles en dehors de leur environnement. Mais abordons ce problème sous un autre angle - si vous écrivez votre script dans des fonctions, il est possible de déclarer une variable comme localet qui peut être éditée.

Du manuel de bash 4.3, localdescription:

... Lorsque local est utilisé dans une fonction, le nom de la variable a une portée visible limitée à cette fonction et à ses enfants ...

Exemple:

#!/bin/bash

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

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

Comme vous pouvez le voir après 3 itérations de la fonction de bouclage, la variable est modifiée.

Sergiy Kolodyazhnyy
la source
Hmm, comportement peu intuitif pour les programmeurs C. Je m'attendrais normalement à outterfaire référence à une instance globale de var.
Ruslan
Cette réponse a quelques défauts. "$var"n'est pas nécessaire dans l'appel outter. Cela ne fait rien. Et local var=0ne fait rien non plus; l'appel ouf outterécrase var, comme vous l'avez dit.
Golar Ramblar
@GolarRamblar que j'ai supprimé "$var"comme argument positionnel pour outter; pour être juste, c'est par habitude. Pouvez-vous élaborer sur la local var=0 partie?
Sergiy Kolodyazhnyy