127

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" 
2
  • Why would you change an argument? A default arg is often helpful, but it does not change the arg. Commented Aug 31, 2024 at 19:26
  • @timo I have a wrapper script around mvn. Mvn will output Test MyClassTest.testMethod failed, but to run just that test, I need to pass -Dtest=MyClassTest#testMethod. (Note period vs hash). So I'd like to change any -Dtest= argument, and still be able to run mvn "$@" to get proper quoting of the other arguments. This lets me copy/paste the output directly, rather than having to edit the middle of the string Commented Aug 14 at 14:44

4 Answers 4

192

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}".

Sign up to request clarification or add additional context in comments.

6 Comments

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?
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.
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.
How can I do the same thing from inside a function? Your solution doesn't seem to work when invoked from inside a function.
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.
|
27

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.

6 Comments

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!!!
@Johnsyweb Agreed. For readability sake, yours is the better method.
"You are better off" always depends on the use case, which is not provided.
@Scott 7 years older and more experience than I was when I wrote this answer, I'm inclined to agree with you.
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.
|
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 

Comments

7

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 the strings "one", "two", and "three", # respectively 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:

# Change just arguments $1, $2, and $3, while keeping arguments $4 and beyond # unchanged 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:

# keep arguments $1 and $2 as-is, change argument $3, and keep arguments $4 and # beyond as-is 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?

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.