1

I'm trying to set the variable 'count' while running the command inside a heredoc together with timeout, but I'm unable to get the 'count' variable outside the heredoc.

How can I achieve that?

  • My script is more complex but I thought getting the answer for that will be enough.

    timeout 10 bash << EOC
       count=$(ls -l /tmp/ | wc -l)
    EOC
    
    echo "count: $count"
    
3

You can't. Variables assigned in a sub-shell have no effect on the parent shell.

Do this instead:

count=$(timeout 10 ls -l /tmp/ | wc -l)
echo "count: $count"
1

What you're trying to do cannot work, you'll have to find another way. When you run another program, it has its own memory space, it can't influence the variables in the current shell. This is true even if the other program happens to be an instance of the same shell that you're running.

Using a here-document instead of bash -c doesn't change this fact. Your example is equivalent to timeout 10 bash -c "count=$(ls -l /tmp/ | wc -l)". (By the way, this runs ls -l /tmp/ | wc -l in the outer shell, not in the instance of bash that you spawn: if you wanted to do the equivalent of timeout 10 bash -c "count=$(ls -l /tmp/ | wc -l)", you'd need to use <<\EOF or <<'EOF' or the like.)

If you only need to get the value of one variable, you can use a command substitution:

count=$(timeout 10 bash -c '…')

If you need to set many variables, or an array, you'll need to do some encoding and decoding. You can get bash to do it for you: declare -p foo bar prints out a way to define foo and bar that's properly quoted for the calling shell.

eval "$(bash -c '…; declare foo bar')"

Note that this declares variables locally, so if you run it in a function, the variables won't be available when the function returns. If you need the variables to be available on return, you need to assign them again with declare -g; this only works for scalars:

f () {
  eval "$(bash -c '…; declare foo bar')"
  declare -g foo="$foo" bar="$bar"
}

Alternatively, if you know that the variables' values can't contain newlines, strip declare -… from the beginning of each line in the output of declare -p.

0

I don't think you can achieve that. The big problem is that you have a bash process evaluating a pipeline, and then storing the value of that pipeline in shell variable count. timeout itself is not a shell builtin on my Arch laptop, so there's a third level of process. Since timeout forks the command it waits on, there's a third level of process.

That is shell-that-invokes-timeout -> timeout -> bash-invoked-by-timeout -> value-of-count

It looks to me like you need to re-arrange your shell script to be able to use the value of count (or whatever it is you want to accomplish).

If you really need the timeout on the command, you should probably send the command's output to a file, and then read that file:

if timeout ls -l /tmp > /tmp/some.well.known.name
then
    VAR=$(wc -l /tmp/some.well.known.name)
else
    : handle timeout
fi
  • the timeout is not that important. what is important to me is the possibility to get the variable 'count' within the EOC and expose it outside EOC. – Asaf Magen May 1 '16 at 16:32
  • 1
    @AsafMagen - then why have the evaluation of ls -l /tmp | wc -l inside a "here document"? Just evaluate it and be done. – Bruce Ediger May 1 '16 at 16:59

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.