Cómo hacer que una variable de un subshell esté disponible en el shell principal

11

He escrito un script rápido y sucio para cronometrar algunos informes de un servicio 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

Necesito ajustar el timecomando en un subshell para poder redirigir su salida, pero eso hace que el valor de $responseno esté disponible para el shell principal. ¿Cómo soluciono este problema?

iconoclasta
fuente

Respuestas:

11

No puede llevar el valor de una variable de una subshell a su padre, no sin realizar una clasificación propensa a errores y una comunicación engorrosa.

Afortunadamente, no necesitas una subshell aquí. La redirección solo requiere agrupación de comandos con { … }, no una subshell.

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

(No olvide las comillas dobles alrededor de las sustituciones variables ).

Gilles 'SO- deja de ser malvado'
fuente
Es curioso que cada lenguaje de programación pueda hacer eso. Ya pasé 1 hora buscando en Google sin una solución posible en Shell. Estoy decepcionado.
A Kra
1
@ToKra No. Ningún lenguaje de programación puede hacer esto. (Casi) cualquier lenguaje de programación puede llevar el valor de una variable desde una subrutina o bloque de instrucciones a su padre, y eso es precisamente lo que explico en mi respuesta: use la agrupación de comandos en { … }lugar de una subshell ( … ).
Gilles 'SO- deja de ser malvado'
4

Compañeros usuarios de U&L: antes de rechazar mi respuesta para usar el estilo C con la main()función, visite este enlace: https://unix.stackexchange.com/a/313561/85039 El uso de las funciones principales en los scripts es una práctica común, utilizada por muchos profesionales en el campo.


Como señaló Gilles, las subcapas no pueden hacer que las variables estén disponibles fuera de su entorno. Pero abordemos este problema desde otro ángulo: si escribe su script en funciones, es posible declarar variables como localy eso se puede editar.

Del manual de bash 4.3, localdescripción:

... Cuando se usa local dentro de una función, hace que el nombre de la variable tenga un alcance visible restringido a esa función y sus elementos secundarios ...

Ejemplo:

#!/bin/bash

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

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

Como puede ver después de 3 iteraciones de la función de bucle, la variable se modifica.

Sergiy Kolodyazhnyy
fuente
Hmm, comportamiento poco intuitivo para programadores en C. Normalmente esperaría outterreferirme a una instancia global de var.
Ruslan
Esta respuesta tiene algunos defectos. "$var"No es necesario en la llamada outter. No hace nada Y tampoco local var=0hace nada; la llamada ouf outtersobrescribe var, como usted dijo.
Golar Ramblar
@GolarRamblar que he eliminado "$var"como argumento posicional para outter; para ser justos, esto es por costumbre. ¿Se puede elaborar en cuanto a local var=0 parte?
Sergiy Kolodyazhnyy