Reputation: 2304
Is there a way to set the positional parameters of a bash script from within a function?
In the global scope one can use set -- <arguments>
to change the positional arguments, but it doesn't work inside a function because it changes the function's positional parameters.
A quick illustration:
# file name: script.sh
# called as: ./script.sh -opt1 -opt2 arg1 arg2
function change_args() {
set -- "a" "c" # this doesn't change the global args
}
echo "original args: $@" # original args: -opt1 -opt2 arg1 arg2
change_args
echo "changed args: $@" # changed args: -opt1 -opt2 arg1 arg2
# desired outcome: changed args: a c
Upvotes: 11
Views: 3109
Reputation: 130
I was searching for this myself and came up with the below, i.e. return a space separated string of the array (sorry, positional parameters) from the function by printing it to stdout and capture it with command substitution, then parse the string word for word and append it back into the positional arguments. Clear as mud!
Obviously won't work for args with spaces in them below but that's of course possible to workaround too.
#!/bin/sh
fn() {
set -- d e f
res=""
for i; do
res="${res:+${res} }${i}"
done
printf "%s\n" "$res"
}
set -- a b c
rv=$(fn)
set --
for word in $rv; do
set -- "$@" "$word"
done
for i; do
echo positional parms "$i"
done
Upvotes: 0
Reputation: 2585
As stated before, the answer is no, but if someone need this there's the option of setting an external array (_ARRAY
), modifying it from within the function and then using set -- ${_ARRAY[@]}
after the fact. For example:
#!/bin/bash
_ARGS=()
shift_globally () {
shift
_ARGS=$@
}
echo "before: " "$@"
shift_globally "$@"
set -- "${_ARGS[@]}"
echo "after: " "$@"
If you test it:
./test.sh a b c d
> before: a b c d
> after: b c d
It's not technically what you're asking for but it's a workaround that might help someone who needs a similar behaviour.
Upvotes: 5
Reputation: 77127
Not really. A function actually has its own scope. A parameter assigned in a function is global by default, but if you explicitly declare it it has local scope:
foo() {
x=3 # global
declare y # local
...
}
The positional parameters are always local to the function, so anything you do to manipulate them will only take effect within the function scope.
Technically, you can always use recursion to solve this issue. If you can confidently call the original script again, you can reorder the arguments that way. E.g.
declare -i depth
outer() {
if (( depth++ < 1 )); then
outer "${@:4:$#}" "${@:1:3}"
return
fi
main "$@"
}
main() {
printf '%s\n' "$@"
}
outer "$@"
This seems like an error prone and confusing solution to a problem I don't understand, but it essentially works.
Upvotes: 1