unshift args after calling shift 1
Solution 1
You could save your arguments in an array:
args=( "$@" ) # use double quotes
shift 1
if foo; then
node foo.js "$@"
elif bar; then
node bar.js "$@"
elif baz; then
node baz.js "$@"
else
node default.js "${args[@]}"
fi
Solution 2
You never need to use shift 1
in the first place. Just use the positional arguments and slice around their indices to pass the arguments.
first_arg="$1"
Once you do this, the rest of the arguments can be accessed as "${@:2}"
. The notation is a way to represent from positional argument 2 to till the end of the list.
Using the construct for your example would be to do
node foo.js "${@:2}"
and for the final else
part do as
node default.js "$1" "${@:2}"
which is the same as doing "$@"
as there is no positional argument shift done.
Solution 3
The thing you tried:
first_arg=$1
shift
# ...later...
else
node default.js "$first_arg" "$@"
fi
This would have been identical to your first variant provided that there are at least one command line argument.
With no command line arguments "$first_arg"
would still be an empty string, and therefore an argument, whereas "$@"
would not generate even an empty string.
If your Node application accepts an empty argument on the command line, then this may make the application's behave different in your two code variants.
If calling your script with no command line arguments is a valid thing to do, you could do
node default.js ${first_arg:+"$first_arg"} "$@"
where ${first_arg:+"$first_arg"}
would expand to nothing if first_arg
is empty or unset, but to "$first_arg"
if first_arg
was set to a non-empty string. Or, if you want to cause an explicit failure for an unset or empty variable:
node default.js "${first_arg:?Error, no command line arguments}" "$@"
Alternatives includes making a copy of $@
as a bash
array as Jesse_b shows in his answer.
Putting $first_arg
back into $@
with
set -- "$first_arg" "$@"`
would not work as this would include an empty argument as $1
in $@
if the command line argument was missing.
With
set -- ${first_arg:+"$first_arg"} "$@"`
you would "unshift" nothing if $first_arg
was empty or if the variable did not exist.
Note that shift is the same as shift 1 and that single statements don't need to be terminated by ; unless followed by another statement on the same line (newline is a command terminator, just like ;).
Related videos on Youtube
![Alexander Mills](https://i.stack.imgur.com/5Oh7r.jpg?s=256&g=1)
Alexander Mills
Updated on September 18, 2022Comments
-
Alexander Mills almost 2 years
I have this scenario
first_arg="$1"; if foo; then shift 1; node foo.js "$@" elif bar; then shift 1; node bar.js "$@" elif baz; then shift 1; node baz.js "$@" else node default.js "$@" fi
I would like to turn the above into this:
first_arg="$1"; shift 1; if foo; then node foo.js "$@" elif bar; then node bar.js "$@" elif baz; then node baz.js "$@" else unshift 1; node default.js "$@" fi
but I am not sure if there is an operator like unshift, which I just made up. One workaround might be this:
node default.js "$first_arg" "$@"
but when I tried that, I got weird behavior.
-
ilkkachu almost 6 yearsBut if there are no arguments (
$1
is unset), then"$first_arg"
will expand to an empty string, and the result is different than whenshift
is omitted and"$@"
used -
Kusalananda almost 6 years@ilkkachu Yes, that might be right. In that case a check at the top for
[ "$#" -ge 1 ]
or something similar might be in order. -
Dennis almost 6 years
"$@"
is better than"$1" "${@:2}"
, which will create an extra argument if$@
is empty. -
Kusalananda almost 6 years@muru Thanks! I left my Swiss army knife of standard substitutions and expansions elsewhere it seems. Thanks for reminding me!