Print shell arguments in reverse order
Solution 1
eval
is the only portable way to access a positional parameter by its dynamically-chosen position. Your script would be clearer if you explicitly looped on the index rather than the values (which you aren't using). Note that you don't need expr
unless you want your script to run in antique Bourne shells; $((…))
arithmetic is in POSIX. Limit the use of eval
to the smallest possible fragment; for example, don't use eval echo
, assign the value to a temporary variable.
i=$#
while [ "$i" -gt 0 ]; do
if [ "$i" -ne 3 ] && [ "$i" -ne 2 ]; then
eval "value=\${$i}"
echo "Parameter $i is $value"
fi
i=$((i-1))
done
In bash, you can use ${!i}
to mean the value of the parameter whose name is $i
. This works when $i
is either a named parameter or a number (denoting a positional parameter). While you're at it, you can make use of other bash convenience features.
for ((i=$#; i>0; i--)); do
if ((i != 3 && i != 4)); then
echo "Parameter $i is ${!i}"
fi
done
Solution 2
I keep a script reverse
on my path that does this:
#!/bin/sh
if [ "$#" -gt 0 ]; then
arg=$1
shift
reverse "$@"
printf '%s\n' "$arg"
fi
Example usage:
$ reverse a b c '*' '-n'
-n
*
c
b
a
You can also use a function instead of a dedicated script.
Solution 3
Assuming the positional parameters don't contain newline characters:
[ "$#" -gt 0 ] && printf '%s\n' "$@" | #print in order
sed '3,4 d' | #skip 3 and 4
tac #reverse (try tail -r on
#non-GNU systems).
Test:
set 1 2 3 4 5 6
printf '%s\n' "$@" |
sed '3,4 d' |
tac
Test output:
6
5
2
1
Solution 4
This is a correct and non-dangerous use of eval. You fully control the content that you are eval
ing.
If it still gives you bad feelings, then if you don't care about portability, you can use Bash's ${!i}
indirection syntax.
Solution 5
With zsh
:
$ set a 'foo bar' c '' '*'
$ printf '<%s>\n' "${(Oa)@}"
<*>
<>
<c>
<foo bar>
<a>
Oa
is a parameter expansion flag to sort the array elements upon expansion in reverse array indices.
To exclude 3 and 4:
$ printf '<%s>\n' "${(Oa)@[5,-1]}" "${(Oa)@[1,2]}"
<*>
<foo bar>
<a>
Related videos on Youtube
WarrenFaith
Updated on September 18, 2022Comments
-
WarrenFaith almost 2 years
I am a bit stuck. My task is to print the arguments to my script in reverse order except the third and fourth.
What I have is this code:
#!/bin/bash i=$# for arg in "$@" do case $i in 3) ;; 4) ;; *) eval echo "$i. Parameter: \$$i";; esac i=`expr $i - 1` done
As I hate eval (greetings to PHP), I am looking for a solution without it but I am not able to find one.
How can I define the position of the argument dynamically?
PS: No its not a homework, I am learning shell for an exam so I try to solve old exams.
-
WarrenFaith almost 13 yearsso
${!i}
isn't part of the standard syntax? -
WarrenFaith almost 13 yearsI can't use
arg
as they are ordered correctly and not in reverse. To the usage ofexpr
, I am limited to use the standard only. -
Gilles 'SO- stop being evil' almost 13 years@WarrenFaith If your script starts with
#!/bin/bash
, you can use${!i}
and(())
. If you want to stick to standard sh, these aren't available, but$((…))
is. -
WarrenFaith almost 13 yearsOk, I think I can work with this :)
-
Shawn J. Goff almost 13 yearsNo, it's a Bashism. Here are the POSIX parameter expansions: pubs.opengroup.org/onlinepubs/009695399/utilities/…
-
WarrenFaith about 9 yearsno explanation at all, nice. Downvote from me. Explain what your code does and I might change my mind.
-
Stéphane Chazelas about 9 yearsCan be simplified to
for ((i = $# - 1; i >= 0; i--))
-
G-Man Says 'Reinstate Monica' about 9 yearsAm I hallucinating? I thought I saw words in the question that said "print the arguments ... except the third and fourth".
-
Stéphane Chazelas about 9 yearsNote that the antique Bourne shells wouldn't be able to access positional parameters beyond $9 anyway.
-
Stéphane Chazelas about 9 yearsNote that that one (contrary to the other answers posted so far) would also work in the Bourne shell (not that there's any reason to use that shell nowadays)
-
Stéphane Chazelas about 9 years@G-Man, the title says "print arguments in reverse" which this is answering without using eval. Excluding 3 and 4 from that is trivial. I don't think the downvotes are justified. It is also self-explanatory (though as I said could be simplified a great deal).
-
Stéphane Chazelas about 9 years
eval
is not the only portable way as Ryan has shown. -
Gilles 'SO- stop being evil' about 9 years@StéphaneChazelas I meant portable way to access a positional parameter directly, not to perform the task as a whole.Clarified.
-
Stéphane Chazelas about 9 yearsWell, using
shift
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) -
G-Man Says 'Reinstate Monica' about 9 years@StéphaneChazelas: Why quote
$#
? Can it be anything other than a non-negative integer? -
Stéphane Chazelas about 9 years@G-Man, leaving a variable unquoted in list context is invoking the split+glob operator. There's not reason why you'd want to invoke it here. That has nothing to do with the content of the variable. See also unix.stackexchange.com/a/171347 towards the end. Also note that some shells (dash, posh for instance) still inherit IFS from the environment.
-
starfry almost 8 yearsI found that, on Android, I had to declare
local arg=$1