1926

I need to check the existence of an input argument. I have the following script

if [ "$1" -gt "-1" ]
  then echo hi
fi

I get

[: : integer expression expected

How do I check the input argument1 first to see if it exists?

Brad Parks
  • 66,836
  • 64
  • 257
  • 336
user775187
  • 22,311
  • 8
  • 28
  • 36
  • You did not tell us how you invoked this script. most likely, the first parameter was not an integral number. – user1934428 Jul 03 '23 at 10:46
  • _How do I check the input argument1 first to see if it exists?_ : Do you mean "how do I check whether the argument 1 is non-empty?" or do you mean "how do I check whether the number of arguments is greater than zero?". Please clarify this in your question. – user1934428 Jul 03 '23 at 10:48

12 Answers12

3245

It is:

if [ $# -eq 0 ]
  then
    echo "No arguments supplied"
fi

The $# variable will tell you the number of input arguments the script was passed.

Or you can check if an argument is an empty string or not like:

if [ -z "$1" ]
  then
    echo "No argument supplied"
fi

The -z switch will test if the expansion of "$1" is a null string or not. If it is a null string then the body is executed.

vallentin
  • 23,478
  • 6
  • 59
  • 81
phoxis
  • 60,131
  • 14
  • 81
  • 117
  • 86
    I like to do it this way, in terse syntax and still POSIX acceptable. `[ -z "$1" ] && echo "No argument supplied"` I prefer one-liners, as they are easier for me; and it's also faster to check exit value, compared to using `if` – J. M. Becker Jun 02 '12 at 20:38
  • 214
    You probably want to add an `exit 1` at the end of your echos inside the if block when the argument is required for the script to function. Obvious, but worth noting for completeness. – msanford Feb 05 '13 at 16:37
  • 28
    It is possible, though rarely useful, for the first argument to be initialized but empty; `programname "" secondarg third`. The `$#` check unambiguously checks the number of arguments. – tripleee May 06 '13 at 04:40
  • 103
    and for optional args `if [ ! -z "$1" ]; then ...` – gcb Feb 26 '14 at 23:18
  • 64
    For a noob, especially someone who comes from a non-scripting background, it is also important to mention some peculiarities about these things. You could have also mentioned that we need a space after the opening and the closing brace. Otherwise things do not work. I am myself a scripting noob (I come from C background) and found it the hard way. It was only when I decided to copy the entire thing "as is" that things worked for me. It was then I realized I had to leave a space after the opening brace and before the closing one. – AjB Sep 13 '13 at 07:17
  • 6
    @HighOnMeat the reason that is not mentioned is because it would have to be mentioned in each and every place where an if statement is used. And that would just be too much. However, people such as yourself commenting like this is just excellent, as you can see, it looks like you have helped 4 people to learn this. – Matthias May 17 '16 at 11:08
  • 3
    I like to echo error messages to stderr: `echo "No argument supplied" >&2` – Flimm Dec 08 '16 at 09:45
  • 1
    @Flimm, naturally, this is just a demonstration. – phoxis Dec 08 '16 at 16:08
  • 3
    I was hung on this for a bit. Please note that the space after [ and before ] are crucial. if [$# -eq 0] will not work, but if [ $# -eq 0 ] will. – Jon R. Aug 15 '17 at 16:06
  • I prefer oneliners too ```[ -z "${1// }" ] && echo "No argument supplied"``` – fiorentinoing Dec 17 '19 at 11:28
  • @J.M.Becker I tried your one liner but got the error: $1: unbound variable. For me the accepted answer is safer. – Colm Bhandal May 18 '20 at 12:35
  • @ColmBhandal, you have set options that will trigger that responce on an unset var, you'd need to use `"${1:-}"` instead of just the plain `"$1"`. – J. M. Becker May 19 '20 at 21:07
  • @Skaldebane I've turned off the code colouring again, since it was incorrectly marking non-comments as comments. No colouring is much to be preferred to inaccurate colouring. – TRiG Jun 11 '20 at 11:45
  • Just something to keep in mind, if you have `shift $((OPTIND -1))` somewhere in your script, the global variable `$#` will reset to `0`. – Kasper Skytte Andersen Nov 10 '20 at 07:45
  • 1
    `-z "$1` will fail with an "unbound variable" error if `set -u` is used. https://stackoverflow.com/a/40630951 explains how to handle this. – nishanthshanmugham Jun 27 '21 at 15:21
  • 1
    @gcb you must use "-n" option insted of "! -z", as you can see here : https://github.com/koalaman/shellcheck/wiki/SC2236 – Franck Besson Aug 11 '23 at 09:02
  • @FranckBesson correct! thanks for pointing out. I cannot edit the comment anymore :( Maybe edit yours to mention "for optional arguments" and hopefully it will get more votes soon. – gcb Aug 21 '23 at 14:58
477

It is better to demonstrate this way

if [[ $# -eq 0 ]] ; then
    echo 'some message'
    exit 1
fi

You normally need to exit if you have too few arguments.

Skaldebane
  • 95
  • 2
  • 11
Val
  • 1
  • 8
  • 40
  • 64
  • 66
    To know more about the difference between [ ] and [[ ]] see http://stackoverflow.com/questions/3427872/whats-the-difference-between-and-in-bash – Sebastián Grignoli Feb 03 '16 at 19:31
  • 5
    This answer is better than the accepted answer because 1. exiting with an error code is better than continuing with invalid data, and 2. `[[` is normally more reasonable than `[`. – dshepherd Aug 16 '22 at 11:52
147

In some cases you need to check whether the user passed an argument to the script and if not, fall back to a default value. Like in the script below:

scale=${2:-1}
emulator @$1 -scale $scale

Here if the user hasn't passed scale as a 2nd parameter, I launch Android emulator with -scale 1 by default. ${varname:-word} is an expansion operator. There are other expansion operators as well:

  • ${varname:=word} which sets the undefined varname instead of returning the word value;
  • ${varname:?message} which either returns varname if it's defined and is not null or prints the message and aborts the script (like the first example);
  • ${varname:+word} which returns word only if varname is defined and is not null; returns null otherwise.
Aleks N.
  • 6,051
  • 3
  • 42
  • 42
  • 1
    The example above seems to use `${varname?message}`. Is the extra `:` a typo, or does it change behavior? – Eki Mar 02 '17 at 16:52
  • 7
    Eki, the ":" is a builtin command and shorthand for /bin/true in this example. It represents a do-nothing command that basically ignores the arguments it is provided. It is essential in this test in order to keep the interpreter from trying to execute the contents of "$varname" (which you certainly do NOT want to happen). Also worth noting; you can test as many variables with this method as you wish. And all with specific error messages. i.e. `: ${1?"First argument is null"} ${2?"Please provide more than 1 argument"}` – user.friendly Aug 16 '17 at 03:29
  • I have bash scrip file name `sitelog` the show me nginx log file, I want to pass to it argument like `sitelog -c` to clear log file. – Salem Aug 13 '22 at 15:37
76

Try:

 #!/bin/bash
 if [ "$#" -eq  "0" ]
   then
     echo "No arguments supplied"
 else
     echo "Hello world"
 fi
Koen.
  • 25,449
  • 7
  • 83
  • 78
Ranjithkumar T
  • 1,886
  • 16
  • 21
  • 6
    Why do you need double-quotes for `$#` and `0`? – user13107 Dec 12 '14 at 03:00
  • 1
    No problem if we use without double-quotes as like $# and 0 – Ranjithkumar T Dec 15 '14 at 07:15
  • 1
    on windows, mingw this is the only way to go. – Lajos Mészáros Jun 11 '15 at 14:56
  • 3
    This answer provides great starting point for a script I just made. Thanks for showing the `else`, too. – Krista K Jul 30 '15 at 22:46
  • 2
    @user13107 double quoted variables in bash prevent globbing (i.e. expanding filenames like `foo*`) and word splitting (i.e. splitting the contents if the value contains whitespace). In this case it's not necessary to quote `$#` because both of those cases do not apply. Quoting the `0` is also not necessary, but some people prefer to quote values since they are really strings and that makes it more explicit. – Dennis Jan 31 '16 at 20:21
  • All this superfluous quoting, used when not strictly necessary like here, raises redundant questions. – Alex Sep 09 '22 at 07:30
60

Only because there's a more base point to point out I'll add that you can simply test your string is null:

if [ "$1" ]; then
  echo yes
else
  echo no
fi

Likewise if you're expecting arg count just test your last:

if [ "$3" ]; then
  echo has args correct or not
else
  echo fixme
fi

and so on with any arg or var

seorphates
  • 889
  • 8
  • 4
46

Another way to detect if arguments were passed to the script:

((!$#)) && echo No arguments supplied!

Note that (( expr )) causes the expression to be evaluated as per rules of Shell Arithmetic.

In order to exit in the absence of any arguments, one can say:

((!$#)) && echo No arguments supplied! && exit 1

Another (analogous) way to say the above would be:

let $# || echo No arguments supplied

let $# || { echo No arguments supplied; exit 1; }  # Exit if no arguments!

help let says:

let: let arg [arg ...]

  Evaluate arithmetic expressions.

  ...

  Exit Status:
  If the last ARG evaluates to 0, let returns 1; let returns 0 otherwise.
Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
devnull
  • 118,548
  • 33
  • 236
  • 227
  • 3
    -1 this might be the worst method if validating existence of an argument.. plus it can trigger history substitution and potentially do bad things. – user.friendly Aug 16 '17 at 03:42
  • 2
    instead of `exit` which kills my zsh process, I use `return` which does not kill it – Timo Dec 19 '17 at 11:53
  • 1
    Why would `((!$#))` trigger history substitution? – Zhro Sep 25 '19 at 04:30
  • What's about 3rd input argument , e.g I have ffmpeg script to downscale videos, but sometimes I want to cut part of it using `-ss` parameter as 3rd argument on my script, if that `arg` not presented the clip won't be cut. if [ -z "$3" ]; then – Salem Sep 26 '22 at 12:03
37

I often use this snippet for simple scripts:

#!/bin/bash

if [ -z "$1" ]; then
    echo -e "\nPlease call '$0 <argument>' to run this command!\n"
    exit 1
fi
f2cx
  • 521
  • 4
  • 3
  • 1
    So, this is to be used in you need only one argument? – Danijel Oct 30 '18 at 14:52
  • @Danijel No, this is testing if there is an argument in the first position. You could have a $2 or a $3 argument ($0 is the script name being run). This simply ignores any other arguments passed. – Josiah Feb 17 '21 at 14:59
20

More modern

#!/usr/bin/env bash

if [[ $# -gt 0 ]]
  then echo Arguments were provided.
  else echo No arguments were provided.
fi
Serge Stroobandt
  • 28,495
  • 9
  • 107
  • 102
12

If you'd like to check if the argument exists, you can check if the # of arguments is greater than or equal to your target argument number.

The following script demonstrates how this works

test.sh

#!/usr/bin/env bash

if [ $# -ge 3 ]
then
  echo script has at least 3 arguments
fi

produces the following output

$ ./test.sh
~
$ ./test.sh 1
~
$ ./test.sh 1 2
~
$ ./test.sh 1 2 3
script has at least 3 arguments
$ ./test.sh 1 2 3 4
script has at least 3 arguments
Community
  • 1
  • 1
Brad Parks
  • 66,836
  • 64
  • 257
  • 336
4

As a small reminder, the numeric test operators in Bash only work on integers (-eq, -lt, -ge, etc.)

I like to ensure my $vars are ints by

var=$(( var + 0 ))

before I test them, just to defend against the "[: integer arg required" error.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Cwissy
  • 2,006
  • 15
  • 14
  • 1
    Neat trick, but please note: due to bash's inability to handle floats in arithmetic, this method can cause a syntax error and return non-zero which would be a hindrance where errexit is enabled. `var=$(printf "%.0f" "$var")` can handle floats but suffers from the non-zero exit when given a string. If you don't mind an awk, this method I use seems to be the most robust for enforcing an integer: `var=$(<<<"$var" awk '{printf "%.0f", $0}')`. If var is unset, it defaults to "0". If var is a float, it is rounded to the nearest integer. Negative values are also fine to use. – user.friendly Aug 16 '17 at 03:56
1

one liner bash function validation

myFunction() {

    : ${1?"forgot to supply an argument"}
    if [ "$1" -gt "-1" ]; then
        echo hi
    fi

}

add function name and usage

myFunction() {

    : ${1?"forgot to supply an argument ${FUNCNAME[0]}() Usage:  ${FUNCNAME[0]} some_integer"}
    if [ "$1" -gt "-1" ]; then
        echo hi
    fi

}

add validation to check if integer

to add additional validation, for example to check to see if the argument passed is an integer, modify the validation one liner to call a validation function:

: ${1?"forgot to supply an argument ${FUNCNAME[0]}() Usage:  ${FUNCNAME[0]} some_integer"} && validateIntegers $1 || die "Must supply an integer!"

then, construct a validation function that validates the argument, returning 0 on success, 1 on failure and a die function that aborts script on failure

validateIntegers() {

    if ! [[ "$1" =~ ^[0-9]+$ ]]; then
        return 1 # failure
    fi
    return 0 #success

}

die() { echo "$*" 1>&2 ; exit 1; }

Even simpler - just use set -u

set -u makes sure that every referenced variable is set when its used, so just set it and forget it

myFunction() {
    set -u
    if [ "$1" -gt "-1" ]; then
        echo hi
    fi

}
AndrewD
  • 4,924
  • 3
  • 30
  • 32
-2

In my case (with 7 arguments) the only working solution is to check if the last argument exists:

if [[ "$7" == '' ]] ; then
  echo "error"
  exit
fi
Matthew
  • 1,264
  • 1
  • 11
  • 20
r1si
  • 1,136
  • 2
  • 19
  • 34
  • 10
    This is not true. `$7` is the 7th argument (8th if you count `$0` which is the script name), so this does not check if the last argument exists, it checks if the 7th argument exists. – Kevin G. Oct 15 '20 at 12:10
  • 1
    I do agree that this is not a solution to the question, and a sub-optimal solution to a different (and probably avoidable) problem. Seven positional arguments seems heavy. In addition, `exit` without exit status will return the exit status of `echo "error"`, which I expect to be zero. Recommend `shellcheck` and `set -euo pipefail`. I'm going to stop now... – Jack Wasey May 04 '21 at 09:38
  • 1
    While not a unique answer, it is similar to other accepted answers that have several upvotes. It seems that the author may not be a native English speaker, and likely meant that in their case of 7 arguments, this was a working solution. I've edited the answer to reflect that. Suggestions from @JackWasey should definitely be taken into consideration. – Matthew Mar 08 '22 at 17:54