9

I've written a quick-and-dirty script to time some reports from a web service:

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

I need to wrap the time command in a subshell so I can redirect its output, but that makes the value of $response unavailable to the parent shell. How do I get around this problem?

10

You can't bring a variable's value from a subshell to its parent, not without doing some error-prone marshalling and cumbersome communication.

Fortunately, you don't need a subshell here. Redirection only requires command grouping with { … }, not a subshell.

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

(Don't forget double quotes around variable substitutions.)

  • its funny that every programming language can do that. i already spent 1h googling with no possible solution in shell. im disapointed. – To Kra Dec 28 '16 at 17:49
  • 1
    @ToKra No. No programming language can do this. (Almost) any programming language can bring a variable's value from a subroutine or instruction block to its parent, and that's precisely what I explain in my answer: use command grouping { … } instead of a subshell ( … ). – Gilles Dec 28 '16 at 17:51
4

Fellow U&L users: Before downvoting my answer for using C-style with main() function, please visit this link: https://unix.stackexchange.com/a/313561/85039 Using main functions in scripts is a common practice, used by many professionals in the field.


As Gilles pointed out, subshells cannot make variables available outside of their environment. But let's approach this problem from another angle - if you write your script in functions, it's possible to declare variable as local and that can be edited.

From bash 4.3's manual, local description:

...When local is used within a function, it causes the variable name to have a visible scope restricted to that function and its children...

Example:

#!/bin/bash

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

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

As you can see after 3 iterations of the looping function, the variable is modified.

  • Hmm, unintuitive behavior for C programmers. I'd normally expect outter to refer to a global instance of var. – Ruslan May 14 '17 at 16:20
  • This answer has some flaws. "$var" is not needed in the call out outter. It does not do anything. And also local var=0 does not do anything; the call ouf outter does overwrite var, as you stated. – Golar Ramblar Jul 18 '18 at 14:25
  • @GolarRamblar I've removed "$var" as positional argument to outter; to be fair, this is out of a habit. Can you elaborate as to local var=0 part ? – Sergiy Kolodyazhnyy Jul 18 '18 at 14:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.