1

I have the bash script below:

#!/bin/bash
#
[ $# -eq 1 -a $1 = "--help" -o $# -eq 0 ] && {
  echo Help will come here
}

When I run it:

$ ./script 
./script: line 3: [: too many arguments
$ ./script --help
Help will come here

As you can see, when I don't pass parameters ( $# -eq 0 ) it fails with "too many arguments". So, I tested it directly in terminal:

$ a=1;b=2;c=3
$ [ $a -eq 1 -a $b -eq 2 -o $c -eq 3 ] && echo ok
ok
$ [ $a -eq 0 -a $b -eq 2 -o $c -eq 3 ] && echo ok
ok
$ [ $a -eq 0 -a $b -eq 0 -o $c -eq 3 ] && echo ok
ok
$ [ $a -eq 0 -a $b -eq 0 -o $c -eq 0 ] && echo ok
$ [ $a -eq 0 -a $b -eq 2 -o $c -eq 0 ] && echo ok
$ [ $a -eq 1 -a $b -eq 2 -o $c -eq 0 ] && echo ok
ok

So, if it works perfectly in terminal why doesn't it work passing parameters?

Thanks,

4
  • 2
    Use more quotes: [ $# -eq 1 -a "$1" = "--help" -o $# -eq 0 ] (see the quotes around the variable expansion "$1"?). Commented Apr 9, 2017 at 17:57
  • @gniourf_gniourf: Why not just use $# -eq 0 -o "$*" = "--help"? Commented Apr 9, 2017 at 18:49
  • @l'L'l: my point was only about the quotes. There are better answers below ;). Commented Apr 9, 2017 at 19:33
  • @gniourf_gniourf, thank you, you were right about the quotes, I totally forgot them, and I think you were the first to mention it. If you had put as an answer I would've ticked yours as the right one. Commented Apr 11, 2017 at 2:39

4 Answers 4

3

Express your condition like this :

[ $# -eq 1 ] && [ "$1" = "--help" ] || [ $# -eq 0 ]

Actually, [ is a command, and the following elements in the commands are subject to word splitting. If an argument is empty (or contains whitespace and is unquoted), you can run into surprises. Using -a and -o is deprecated.

Please note that, if you want to use the && logical operator (instead of an if statement) before your echo statement, you will need to enclose the above inside braces, else the operator precedence (coupled with lazy evaluation) may yield incorrect results.

{ [ $# -eq 1 ] && [ "$1" = "--help" ] || [ $# -eq 0 ] ; } && { echo...

If you do not mind using Bash-specific syntax, you could also write :

[[ $# -eq 1 && $1 = "--help" || $# -eq 0 ]]

Note that in this case, double-quoting $1 is not required, because the [[ ]] construct is special shell syntax, not a command, and what is inside is not subject to word splitting. Because there is a single test, you do not need to enclose it inside braces before your && { echo....

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

7 Comments

Re: "[[ ]] construct is special shell syntax, not a command, and what is inside is not subject to word splitting", is perhaps not entirely correct. github.com/koalaman/shellcheck/wiki/SC2048. In shellcheck.net using an unquoted $@ or $* prompts this warning while used inside [[ ]], so this may be an exception.
@l'L'l From the Bash manual : "Word splitting and filename expansion are not performed on the words between the [[ and ]]"
It's interesting that the warning comes up on shellcheck.net; I wonder if that should be reported.
Still from the manual, in the section about $@ : "When the expansion occurs within double quotes, each parameter expands to a separate word.". This invaluable behavior, also available for array expansions, can be a little counter-intuitive at first, because double quotes usually enclose a string that will be considered a single word. It should be understood as "expand all arguments, but make sure each argument stays a single word by not submitting it to word splitting".
In shellcheck when trying to use [[ $@ ]] it comes back with ^-- SC2199: Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @)., and then while using [[ $* ]] it returns ^-- SC2048: Use "$@" (with quotes) to prevent whitespace problems., so it is a bit confusing.
|
1

Your entire expression can be simplified to:

function help () {
    printf "%s\n" "help is on it's way."
}

[[ $# -eq 0 || "$*" = "--help" ]] && help ; echo "done." && exit 0 ;

This checks if the total sum of arguments is zero, or the argument(s) equals "--help". If either of those two things are true then it proceeds to the help function, otherwise echo "done" and exit.

3 Comments

This works beautifully @l'L'l. I don't quite understand the difference between [] and [[]]. From what I know [] is a short for the command test.
@Adriano_epifas: In short, [[ is bash's improvement to the [ notation. Here's a detailed comparison: mywiki.wooledge.org/BashFAQ/031. Examples: stackoverflow.com/a/3427931/499581
You're welcome! [[ and [ are actually "test commands" not really "notation" as mentioned... ;_;
0

When you are executing the script without parameter, you are getting the error because your condition is matching with blank character, see below -

$sh -x kk.sh 
+ '[' 0 -eq 1 -a = --help -o 0 -eq 0 ']'
kk.sh: line 3: [: too many arguments

As you can see that there is no value to match.

When you will execute below command in your terminal -

$[ $a -eq 1 -a $b -eq 2 -o $c -eq 3 ] && echo ok
-bash: [: too many arguments
$a=1;b=2;c=3
$[ $a -eq 1 -a $b -eq 2 -o $c -eq 3 ] && echo ok
ok ####it is printing this value bcoz you have set the variable to match, it doesn't matter condition is wrong or right but there is something to match.

To resolve this issue you can use one if condition in the beginning to assign a dummy value if there is no value.

Comments

-1

Try this :-

[ $# -eq 1 ] || [ $1 = "--help" ] || [ $# -eq 0 ]

This way it will automatically display if --help is provided or 1 is typed.

I think $# is creating the problem as both there is $# for first and second conditions

4 Comments

$1 should be quoted, because an empty value or one with whitespace would cause the test to fail due to an incorrect number of arguments after word splitting.
Reuse of $# in two conditions is not the problem here, it is actually required to get the behavior I assume is expected by the OP.
The logical operator between the first two tests should be &&.
what @Fred said is right. the problems was that I didn't quote $1. Thank you

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.