Reputation: 191
For example, instead of doing
start=`commandforstdout | awk {'print $2'}`
end=`commandforstdout | awk {'print $5'}`
Is there a way to store
`commandforstdout | awk {'print $2, $5'}`
into two different variables without having to run the command a second time? Just for the sake of efficiency...
Upvotes: 2
Views: 129
Reputation: 437448
To complement the helpful existing answers:
The reason a straightforward pipeline solution (commandforstdout | read ...
) doesn't work is that all commands in a pipeline are by default run in a subshell, so that the variables that a read
command in a pipeline creates will not be visible after the pipeline.
However, on bash 4.2+, turning on shell option lastpipe
causes the last pipeline segment to run in the current shell, allowing read
to create variables visible to the current shell.
Example:
# bash 4.2+: Turn on shell option that runs the *last* pipeline segment in the
# *current shell* rather than in a *subshell*.
shopt -s lastpipe
# Read the two words piped via stdin into two variables.
# Since option `lastpipe` is on, the variables are created in the *current* shell.
echo 'START END' | read -r start end
echo "[$start] [$end]" # -> '[START] [END]'
Note: lastpipe
only works when job control is turned off, which is true by default in non-interactive shells (e.g., in scripts), but not true in interactive shells; to test the example above interactively, you must turn job control (temporarily) off:
set +m; shopt -s lastpipe && echo 'START END' | read -r start end; echo "[$start] [$end]"; set -m
Upvotes: 2
Reputation: 72639
The posixly portable way to do this without coprocesses or bashisms:
set -- $(commandforstdout | awk '{print $2, $5}')
start=$1 end=$2
This could be even easier if you tell us the output of commandforstdout
. It might be that
set -- $(commandforstdout)
start=$2 end=$5
would even save the expensive fork and pipe. As gniourf_gniourf rightfully noted, pathname expansion may get in the way if the result contains glob characters. Should this be a problem, use set -f
before the first set
and set +f
if you need pathname expansion later on again.
Upvotes: 2
Reputation: 785068
Use read
builtin with process substitution:
read start end < <(commandforstdout | awk {'print $2, $5'})
Or even without awk (thanks to @gniourf_gniourf):
read -r _ start _ _ end _ < <(commandforstdout)
Upvotes: 5