Just assign all of the variables and write the output at the same time.
f() { c= ; echo "${c:=$(date): $((a=3)) $((b=4))}" ; }
Now if you do:
f ; echo "$a $b $c"
Your output is:
Tue Jul 1 04:58:17 PDT 2014: 3 4
3 4 Tue Jul 1 04:58:17 PDT 2014: 3 4
Note that this is fully POSIX portable code. I initially set c=
to the ''
null string because parameter expansion limits concurrent variable assignment + evaluation to either only numeric (like $((var=num))
) or from null or nonexistent values - or, in other words, you can't concurrently set and evaluate a variable to an arbitrary string if that variable is already assigned a value. So I just ensure that it is empty before trying. If I did not empty c
before attempting to assign it the expansion would only return the old value.
Just to demonstrate:
sh -c '
c=oldval a=1
echo ${c:=newval} $((a=a+a))
'
###OUTPUT###
oldval 2
newval
is not assigned to $c
inline because oldval
is expanded into ${word}
, whereas the inline $((
arithmetic=
assignment))
always occurs. But if $c
has no oldval
and is empty or unset...
sh -c '
c=oldval a=1
echo ${c:=newval} $((a=a+a))
c= a=$((a+a))
echo ${c:=newval} $((a=a+a))
'
###OUTPUT###
oldval 2
newval 8
...then newval
is at once assigned to and expanded into $c
.
All other ways to do this involve some form of secondary evaluation. For example, let's say I wished to assign the output of f()
to a variable named name
at one point and var
at another. As currently written, this will not work without setting the var in the caller's scope. A different way could look like this, though:
f(){ fout_name= fout= set -- "${1##[0-9]*}" "${1%%*[![:alnum:]]*}"
(: ${2:?invalid or unspecified param - name set to fout}) || set --
export "${fout_name:=${1:-fout}}=${fout:=$(date): $((a=${a:-50}+1)) $((b=${b:-100}-4))}"
printf %s\\n "$fout"
}
f &&
printf %s\\n \
"$fout_name" \
"$fout" \
"$a" "$b"
I've provided a better formatted example below, but, called as above the output is:
sh: line 2: 2: invalid or unspecified param - name set to fout
Wed Jul 2 02:27:07 PDT 2014: 51 96
fout
Wed Jul 2 02:27:07 PDT 2014: 51 96
51
96
Or with different $ENV
or arguments:
b=9 f myvar &&
printf %s\\n \
"$fout_name" \
"$fout" \
"$myvar" \
"$a" "$b"
###OUTPUT###
Tue Jul 1 19:56:42 PDT 2014: 52 5
myvar
Tue Jul 1 19:56:42 PDT 2014: 52 5
Tue Jul 1 19:56:42 PDT 2014: 52 5
52
5
Probably the trickiest thing to get right when it comes to twice evaluating is to ensure that variables don't break quotes and execute random code. The more times a variable is evaluated the harder it gets. Parameter expansion helps a great deal here, and using export
as opposed to eval
is far safer.
In the above example f()
first assigns $fout
the ''
null string and then sets positional params to test for valid variable names. If both tests do not pass a message is emitted to stderr
and the default value of fout
is assigned to $fout_name
. Regardless of the tests, however, $fout_name
is always assigned to either fout
or the name you specify and $fout
and, optionally, your specified name are always assigned the output value of the function.
To demonstrate this I wrote this little for
loop:
for v in var '' "wr;\' ong"
do sleep 10 &&
a=${a:+$((a*2))} f "$v" || break
echo "${v:-'' #null}"
printf '#\t"$%s" = '"'%s'\n" \
a "$a" b "$b" \
fout_name "$fout_name" \
fout "$fout" \
'(eval '\''echo "$'\''"$fout_name"\")' \
"$(eval 'echo "$'"$fout_name"\")"
done
It plays around some with variable names and parameter expansions. If you have a question just ask. That only runs the same few lines in the function already represented here. It's worth mentioning at least though that the $a
and $b
variables behave differently depending on whether they are defined at invocation, or set already. Still, the for
does almost nothing but format the data set and provided by f()
. Have a look:
###OUTPUT###
Wed Jul 2 02:50:17 PDT 2014: 51 96
var
# "$a" = '51'
# "$b" = '96'
# "$fout_name" = 'var'
# "$fout" = 'Wed Jul 2 02:50:17 PDT 2014: 51 96'
# "$(eval 'echo "$'"$fout_name"\")" = 'Wed Jul 2 02:50:17 PDT 2014: 51 96'
sh: line 2: 2: invalid or unspecified param - name set to fout
Wed Jul 2 02:50:27 PDT 2014: 103 92
'' #null
# "$a" = '103'
# "$b" = '92'
# "$fout_name" = 'fout'
# "$fout" = 'Wed Jul 2 02:50:27 PDT 2014: 103 92'
# "$(eval 'echo "$'"$fout_name"\")" = 'Wed Jul 2 02:50:27 PDT 2014: 103 92'
sh: line 2: 2: invalid or unspecified param - name set to fout
Wed Jul 2 02:50:37 PDT 2014: 207 88
wr;\' ong
# "$a" = '207'
# "$b" = '88'
# "$fout_name" = 'fout'
# "$fout" = 'Wed Jul 2 02:50:37 PDT 2014: 207 88'
# "$(eval 'echo "$'"$fout_name"\")" = 'Wed Jul 2 02:50:37 PDT 2014: 207 88'
$a
and$b
are local variables in yourf
function. You couldexport
them, but that seems sketchy. – HalosGhost Jun 28 '14 at 20:00…; f; echo $a; …
results in3
being echoed, sof
is modifying a shell variable (and not just its own local variable). – Scott - Слава Україні Jun 28 '14 at 20:38