With basically any shell:
printf '{ PS4=\${$(($#-$x))}; } 2>&3; 2>&1\n%.0s' |
x=LINENO+1 sh -sx "$@" 3>/dev/null
And you don't need to use subshells. For example:
set -x a b c
{ last= PS4=\${last:=\${$#}}; set +x; } 2>/dev/null
echo "$last"
...prints...
c
And here is a shell function which can set a shell alias
for you that will print the arguments either forward or backward:
tofro() case $1 in (*[!0-9]*|'') ! :;;(*) set "$1"
until [ "$1" -eq "$(($#-1))" ] &&
shift && alias args=":; printf \
\"%.\$((\$??\${#*}:0))s%.\$((!\$??\${#*}:0))s\n\" $* "
do [ "$#" -gt 1 ] &&
set "$@ \"\${$#}\" " '"${'"$((1+$1-$#))"'}"' ||
set "$1" '"$1" "${'"$1"'}"'
done; esac
It doesn't attempt to store the literal values for any arguments, but rather it puts a string like this in the args
alias
:
:;printf "%.$(($??${#*}:0))s%.$((!$??${#*}:0))s\n" \
"$1" "${3}" "${2}" "${2}" "${3}" "${1}"
...and so stores only references to the parameters backwards and forwards. It will store up to a count as given it as an argument. And so the above alias
was generated like:
tofro 3
printf
's behavior is affected based on the return value of the previous command - which is always :
the null command, and so usually true. printf
will skip half of its arguments each time it prints - which will, by default, get the arguments printed out from smallest numbered to largest. However, if you just do:
! args
...it prints them in reverse.
Because the alias does not store any literal values, its value remains static while the actual args might change, but it will still reference as many as it might. For example:
set one two three
tofro 3
args; ! args
shift; args; ! args
...which prints...
one
two
three
three
two
one
two
three
three
two
But resetting the alias can be done like:
tofro 2
args; ! args
...and so it prints...
two
three
three
two
arg
as they are ordered correctly and not in reverse. To the usage ofexpr
, I am limited to use the standard only. – WarrenFaith Sep 25 '11 at 16:07#!/bin/bash
, you can use${!i}
and(())
. If you want to stick to standard sh, these aren't available, but$((…))
is. – Gilles 'SO- stop being evil' Sep 25 '11 at 16:10eval
is not the only portable way as Ryan has shown. – Stéphane Chazelas Jun 12 '15 at 15:04shift
is still another option (and more portable since${10}
doesn't work in the Bourne shell). (likegetn() { shift "$1"; n=$1; }; getn 14 "$@"
to get the 14th parameter) – Stéphane Chazelas Jun 12 '15 at 16:04