793

I want my script to be able to take an optional input,

e.g. currently my script is

#!/bin/bash
somecommand foo

but I would like it to say:

#!/bin/bash
somecommand  [ if $1 exists, $1, else, foo ]
3
  • 3
    See also unix.stackexchange.com/questions/122845/… Commented Mar 28, 2016 at 10:12
  • 2
    ...and stackoverflow.com/questions/2013547/… Commented Mar 28, 2016 at 10:26
  • 1
    I want to say that the this subject is not about the optional argument but a positional argument with default value. This terminology gives much confusion. "Optional argument" means it would be ok whether those arguments exist in the command line or not. Commented Feb 16, 2021 at 3:40

10 Answers 10

1123

You could use the default-value syntax:

somecommand ${1:-foo}

The above will, as described in Bash Reference Manual - 3.5.3 Shell Parameter Expansion [emphasis mine]:

If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted.

If you only want to substitute a default value if the parameter is unset (but not if it's null, e.g. not if it's an empty string), use this syntax instead:

somecommand ${1-foo}

Again from Bash Reference Manual - 3.5.3 Shell Parameter Expansion:

Omitting the colon results in a test only for a parameter that is unset. Put another way, if the colon is included, the operator tests for both parameter’s existence and that its value is not null; if the colon is omitted, the operator tests only for existence.

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

9 Comments

Please note the semantic difference between the above command, "return foo if $1 is unset or an empty string", and ${1-foo}, "return foo if $1 is unset".
Can you explain why this works? Specially, what's the function/purpose of the ':' and '-'?
@jwein001: In the answer submitted above, a substitution operator is used to return a default value if the variable is undefined. Specifically, the logic is "If $1 exists and isn't null, return its value; otherwise, return foo." The colon is optional. If it's omitted, change "exists and isn't null" to only "exists." The minus sign specifies to return foo without setting $1 equal to 'foo'. Substitution operators are a subclass of expansion operators. See section 6.1.2.1 of Robbins and Beebe's Classic Shell Scripting [O'Reilly] (shop.oreilly.com/product/9780596005955.do)
@Jubbles or if you don't want to buy an entire book for a simple reference... tldp.org/LDP/abs/html/parameter-substitution.html
This answer would be even better if it showed how to make the default be the result of running a command, as @hagen does (though that answer is inelegant).
|
574

You can set a default value for a variable like so:

somecommand.sh

#!/usr/bin/env bash

ARG1=${1:-foo}
ARG2=${2:-'bar is'}
ARG3=${3:-1}
ARG4=${4:-$(date)}

echo "$ARG1"
echo "$ARG2"
echo "$ARG3"
echo "$ARG4"

Here are some examples of how this works:

$ ./somecommand.sh
foo
bar is
1
Thu 19 May 2022 06:58:52 ADT

$ ./somecommand.sh ez
ez
bar is
1
Thu 19 May 2022 06:58:52 ADT

$ ./somecommand.sh able was i
able
was
i
Thu 19 May 2022 06:58:52 ADT

$ ./somecommand.sh "able was i"
able was i
bar is
1
Thu 19 May 2022 06:58:52 ADT

$ ./somecommand.sh "able was i" super
able was i
super
1
Thu 19 May 2022 06:58:52 ADT

$ ./somecommand.sh "" "super duper"
foo
super duper
1
Thu 19 May 2022 06:58:52 ADT

$ ./somecommand.sh "" "super duper" hi you
foo
super duper
hi
you

6 Comments

Ah, ok. The - confused me (is it negated?).
Nope - that's just a weird way bash has of doing the assignment. I'll add some more examples to clarify this a bit... thanks!
It looks this is the limit of bash argument. Using python terminology, this is not optional, it is positional. It is just a positional argument with default values. Is there no really optional argument in bash?
you may be able to use getopts to get what you want - Here's 2 stackoverflow answers that go into greater detail: dfeault values and inside a function
Note also, that to confuse even more (with JSON/python dicts) no space is permitted anywhere, e.g. after the colon and/or after/before the dash... where is the principle of least astonishment here?:)
|
68
if [ ! -z $1 ] 
then 
    : # $1 was given
else
    : # $1 was not given
fi

5 Comments

Technically, if you pass in an empty string '' that might count as a parameter, but your check will miss it. In that case $# would say how many parameters were given
-n is the same as ! -z.
I get different results using -n and ! -z so I would say that is not the case here.
because you failed to quote the variable, [ -n $1 ] will always be true. If you use bash, [[ -n $1 ]] will behave as you expect, otherwise you must quote [ -n "$1" ]
For ones like me: forget the ":" in the code, it's not required, replace it with your real commands!
36

You can check the number of arguments with $#

#!/bin/bash
if [ $# -ge 1 ]
then
    $1
else
    foo
fi

Comments

13

please don't forget, if its variable $1 .. $n you need write to a regular variable to use the substitution

#!/bin/bash
NOW=$1
echo  ${NOW:-$(date +"%Y-%m-%d")}

2 Comments

Brad's answer above proves that argument variables can also be substituted without intermediate vars.
+1 for noting the way to use a command like date as the default instead of a fixed value. This is also possible: DAY=${1:-$(date +%F -d "yesterday")}
11

This allows default value for optional 1st arg, and preserves multiple args.

> cat mosh.sh
  set -- ${1:-xyz} ${@:2:$#} ; echo $*    
> mosh.sh
  xyz
> mosh.sh  1 2 3
  1 2 3

Comments

6

For optional multiple arguments, by analogy with the ls command which can take one or more files or by default lists everything in the current directory:

if [ $# -ge 1 ]
then
    files="$@"
else
    files=*
fi
for f in $files
do
    echo "found $f"
done

Does not work correctly for files with spaces in the path, alas. Have not figured out how to make that work yet.

Comments

5

It's possible to use variable substitution to substitute a fixed value or a command (like date) for an argument. The answers so far have focused on fixed values, but this is what I used to make date an optional argument:

~$ sh co.sh
2017-01-05

~$ sh co.sh 2017-01-04
2017-01-04

~$ cat co.sh

DAY=${1:-$(date +%F -d "yesterday")}
echo $DAY

Comments

2
while getopts a: flag
do
    case "$flag" in
        a) arg1=${OPTARG};;
    esac
done

echo "Print optional argument: $arg1"

if [[ -z "$arg1" ]]; then
    ARG=DEFAULT_VAL
else
    ARG=$arg1
fi  

#Run using below command (eg: file name : runscript.sh)
bash runscript.sh -a argument_val &  

Comments

-1

When you use ${1: } you can catch the first parameter(1) passed to your function or(:) you can catch a blank space like a default value.

For example. To be able to use Laravel artisan, I put this into my .bash_aliases file:

artisan() {
    docker exec -it **container_name** php artisan ${1: } ${2: } ${3: } ${4: } ${5: } ${6: } ${7: }
}  

and now, I can just type in command line:

  • artisan -- and all parameters(${1: } ${2: } ${3: } ${4: } ${5: } ${6: } ${7: }) will be just blank spaces
  • artisan cache:clear -- and the first parameter ( ${1: } ) will be cache:clear and all the others will be just blank spaces

So, in this case I can pass 7 parameters optionally.

I hope it can help someone.

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.