How can I change a command line argument in Bash?
Asked Answered
C

4

119

Is there a way to change the command line arguments in a Bash script? For example, a Bash script is invoked like this:

./foo arg1 arg2

Is there a way to change the value of arg1 within the script? Something like:

$1="chintz"
Charla answered 28/1, 2011 at 11:27 Comment(0)
H
184

You have to reset all arguments. To change, e.g., argument $3:

set -- "${@:1:2}" "new_arg3" "${@:4}"

Basically you set all arguments to their current values, except for the one(s) that you want to change. set -- is also specified by POSIX 7.

The "${@:1:2}" notation is expanded to the two (hence the 2 in the notation) positional arguments, starting from offset 1 (i.e. $1). It is a shorthand for "$1" "$2" in this case, but it is much more useful when you want to replace, e.g., "${17}".

Histamine answered 28/1, 2011 at 11:29 Comment(6)
so, in order to change $3, I must change $1 and $2 as well, is it? And change them to what? What does "reset" mean?Charla
Thanks for the trick! I had difficulty using this for filenames with embedded spaces. For anyone else who may run into that problem, try putting eval at the front of the line per this.Ectomorph
This is ok when you know the position of the parameter you want to change. What if you actually don't know it and need to change it dynamically? I tried with $ set -- "${:1:$pivot}" but it doesn't accept a variable there.Loathsome
How can I do the same thing from inside a function? Your solution doesn't seem to work when invoked from inside a function.Huntley
You cannot change the positional parameters of a script from within a function - the function's own arguments hide them. You can, however, store them in an array and operate on that.Histamine
I needed some additional help to understand this answer, so I wrote an explanatory and more-thorough answer here. Thanks for your work to get me started.Shigella
P
26

Optimising for legibility and maintainability, you may be better off assigning $1 and $2 to more meaningful variables (I don't know, input_filename = $1 and output_filename = $2 or something) and then overwriting one of those variables (input_filename = 'chintz'), leaving the input to the script unchanged, in case it is needed elsewhere.

Paving answered 28/1, 2011 at 12:42 Comment(6)
I wanted an approach where I could alter one of the input arguments itself. I needed to do that since I wanted to return a value from the script. The answer suggested by thkala worked well. Thanks for the response!!!Charla
@Johnsyweb Agreed. For readability sake, yours is the better method.Finish
"You are better off" always depends on the use case, which is not provided.Indeterminate
@Scott 7 years older and more experience than I was when I wrote this answer, I'm inclined to agree with you.Paving
Example where this would not work (apart from using something like $FIRST_PARAMETER): having the use and meaning of the parameter depending on the actual value.Semmes
@Charla I cannot imagine a situation where you cannot pass the value of a variable and then return that from a script. Do you mind clarifying in what scenario this answer would not work?Gwenni
O
9

I found the answer by thkala very helpful, so I have used the idea and expanded on it slightly to enable me to add defaults for any argument which has not been defined. For example:

# Set defaults for the passed arguments (if any) if not defined.
#
arg1=${1:-"default-for-arg-1"}
arg2=${2:-"default-for-arg-2"}
set -- "${arg1}" "${arg2}"
unset arg1 arg2
Ovular answered 29/10, 2020 at 11:30 Comment(0)
S
4

How to change command-line arguments $1, $2, etc. in Bash using set --

As an addition to the main answer on Ask Ubuntu by @Radu Rădeanu here, and to the main answer by @thkala on Stack Overflow here, I'd like to add a really simple example to set multiple arguments at once like this:

# Set arguments `$1`, `$2`, and `$3` to these respective values
set -- "one" "two" "three"
echo "$1 $2 $3"

Output:

one two three

If you have additional arguments after the arguments you want to change, you can keep them by slicing them onto the end, like this:

set -- "one" "two" "three" "${@:4}"

I explain the "${@:4}" slicing syntax in my answer here: Unix & Linux: Bash: slice of positional parameters. See that answer for much more detail on bash array slicing. Here is a small snippet from it:

# Array slicing basic format 1: grab a certain length starting at a certain
# index
echo "${@:2:5}"
#         │ │
#         │ └────> slice length
#         └──────> slice starting index

# Array slicing basic format 2: grab all remaining array elements starting at a
# certain index through to the end
echo "${@:2}"
#         │
#         │
#         └──────> slice starting index

So, in the main answer by @thkala, which contains this:

set -- "${@:1:2}" "new_arg3" "${@:4}"

...the explanation is that "${@:1:2}" grabs the first 2 arguments, "new_arg3" inserts that as a new argument 3, and "${@:4}" grabs all arguments from 4 to the end, thereby changing only argument 3 and keeping all other arguments as they were.

For help on the set built-in bash command, see help set or look online here: https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#The-Set-Builtin

help set shows the following for the -- argument:

  --  Assign any remaining arguments to the positional parameters.
      If there are no remaining arguments, the positional parameters
      are unset.

See also

  1. My shorter answer on Ask Ubuntu here: How to change the value of an argument in a script?
Shigella answered 22/4, 2023 at 18:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.