115

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"
Pang
  • 9,564
  • 146
  • 81
  • 122
Sriram
  • 10,298
  • 21
  • 83
  • 136

4 Answers4

182

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

Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
thkala
  • 84,049
  • 23
  • 157
  • 201
  • 1
    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? – Sriram Jan 28 '11 at 11:31
  • 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](http://www.linuxjournal.com/content/bash-preserving-whitespace-using-set-and-eval). – cxw Oct 30 '13 at 20:06
  • 2
    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. – Daniele Segato May 09 '14 at 08:16
  • 1
    How can I do the same thing from inside a function? Your solution doesn't seem to work when invoked from inside a function. – Hashken May 06 '15 at 18:14
  • 3
    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. – thkala May 06 '15 at 23:11
  • I needed some additional help to understand this answer, so I wrote [an explanatory and more-thorough answer here](https://stackoverflow.com/a/76081159/4561887). Thanks for your work to get me started. – Gabriel Staples Apr 22 '23 at 18:02
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.

johnsyweb
  • 136,902
  • 23
  • 188
  • 247
  • 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!!! – Sriram Feb 11 '11 at 08:00
  • 3
    @Johnsyweb Agreed. For readability sake, yours is the better method. – Dss Mar 18 '14 at 13:14
  • 1
    "You are better off" always depends on the use case, which is not provided. – Him Apr 04 '18 at 19:36
  • 2
    @Scott 7 years older and more experience than I was when I wrote this answer, I'm inclined to agree with you. – johnsyweb Apr 05 '18 at 12:48
  • 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. – Igor Stoppa Sep 16 '18 at 13:54
  • @Sriram 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? – BUFU Oct 26 '20 at 15:31
8

I know this is an old one but 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

I hope this is of use to someone else.

mr_thinkit
  • 91
  • 2
  • 7
0

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?
Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265