Bash – Shift bash arguments from the right

bash

I have a script that users will prefix, rather than append, arguments to, i.e. they might call command C, command B C, command A B C, and so on.

I'd like to be able to simply shift over these arguments from the right, the same way you might shift them from the left with shift.

I'm imaginging a shift-right command that behaves like so:

echo "$@"    # A B C
shift-right
echo "$@"    # A B
shift-right
echo "$@"    # A
shift-right
echo "$@"    # 
echo "$#"    # 0

Is there a clean way to accomplish this? I know I can work around it, but a shift-like solution would be much nicer and simpler.


In response to the XY-problem comment, my specific use case is a command that takes either a port or a host and port, e.g. command 123 or command remotehost 123. I don't want users to have to specify these in reverse order (port then host).

It would be fairly clean to say something like (untested, obviously):

port=${@: -1}
shift-right
host=${1:-localhost}

Really though, I'm curious about the question in general, even if there's a better way to solve this specific example.

Here's one reasonably clean way to handle the two-argument case without shift-right, just for reference:

port=${@: -1}
host=${2:+$1}
host=${host:-localhost}

But hopefully you can appreciate how that becomes more cludgy as the number of arguments increases.

Best Answer

If the list of positional parameters is:

$ set -- 0wer 1wdfg 2erty 333 4ffff 5s5s5

Then this will print the arguments without the last:

$ echo "${@:1:$#-1}"
0wer 1wdfg 2erty 333 4ffff

Of course, the parameters could be set to that as well:

$ set -- "${@:1:$#-1}"
$ echo $@
0wer 1wdfg 2erty 333 4ffff

That works in bash version 2.0 or above.

For other simpler shells, you need a (somewhat tricky) loop to remove the last parameter:

unset b; for a; do set -- "$@" ${b+"$b"}; shift; b="$a"; done