So I'm trying to write a simple script that takes two stdin variables then tests to see if the string value of those two variables are equal to a set string. Here is my code:
echo "First Name:"
read fN
echo "Last Name"
read lN
if (( $fN + $lN=="louis smith" ))
then
echo "You are root, you may continue."
else
echo "Access denied, you are not root."
first
When I run the script and enter the first name as "louis" and the last name as "smith" I get this error:
((: louis + smith==louis smith : syntax error in expression (error token is "smith ")
Then it tells me that I am not root when I clearly am. xD
Any input helps :D
(( ... )) is for arithmetic expressions, you can't use it to compare strings. And + is for adding numbers, not concatenating strings. Use [[ ... ]] for conditional expressions.
if [[ "$fN $lN" = "louis smith" ]]
In addition to the other answer, you need to make sure you familiarize yourself with each of the ways the conditional can be properly written. There is no magic, just read the appropriate section of man bash. The [[ test is actually a Reserved Word in bash and is a bashism (it works in bash, but not in POSIX shell). It is the most flexible and forgiving test construct for bash and should be used if portability is not a concern. Make sure you understand the difference in quoting requirements, word splitting and pathname expansion, between the [[ test clause and the [ and test builtins.
In addition to [[, you can also make use of [ and test for testing. ([ and test are equivalent). The following are also correct and portable to POSIX shell:
if [ "$fN $lN" = "louis smith" ]
if test "$fN $lN" = "louis smith"
To answer your original question directly, you test for more than one condition by either using the -a (and) or -o (or) within the test expression itself (older syntax) or by separating multiple test expressions with && (and) or || (or). For instance to check for multiple conditions using your example you could do:
if [ "$fN" = "louis" -a "$LN" = "smith" ]
or
if test "$fN" = "louis" -a "$LN" = "smith"
Written using && or ||:
if [ "$fN" = "louis" ] && [ "$LN" = "smith" ]
or
if test "$fN" = "louis" && test "$LN" = "smith"
(note: no matter which you use you must always leave a space between the test expression and the [ and ] and a space or a newline (or line break indicator ;) between if test "$a" = b ; then...)
Related
Below is my script, I am not getting the right output, I am typing alice I am getting bob as output, but when i am typing bob I should get alice as output, but i am getting bob as output. Kindly let me know what's the error in my script
#!/bin/bash
echo "Enter your name"
read a
if [ $a==alice ];
then
echo "bob"
elif [ $a==bob ];
then
echo "alice"
else
echo "STDERR"
fi
Wrong syntax, should be:
[[ $a == alice ]]
Check: http://tldp.org/LDP/abs/html/comparison-ops.html
You should put spaces around your == operator, so that it's a three-argument form of [.
Without the spaces, it's using the one-argument form which gives true if the string length is non-zero (as it is with the string bob==alice).
Spaces are required surrounding your == expression. When using the [ test operator or using the keyword test, you will want to use the = for posix compliance and portability:
[ "$a" = alice ] # remember to quote your variable when comparing strings
While == is permissive with [ or test, you will lose portability. Alternatively, if you are using [[ operator, then == is correct:
[[ "$a" == alice ]] # quoting is not required, but is good practice.
I am trying to make a bash script with the output based on the input.
My code looks like this:
#!/bin/bash
echo "Letter:"
read a
if a=3
then
echo "LOL"
fi
if a=4
then
echo "ROFL"
fi
But when I enter 3 or 4, I get both LOL and ROFL.
Is there a way for me to get LOL for 3 and ROFL for 4?
Sorry if I'm using incorrect terms and stuff, I'm new to bash scripting.
In bash, a=3 is an assignment, not a test. Use, e.g.:
if [ "$a" = 3 ]
Inside [...], the equal sign tests for string (character) equality. If you want to test for numeric value instead, then use '-eq` as in:
if [ "$a" -eq 3 ]
The quotes around "$a" above are necessary to avoid an "operator" error when a is empty.
bash also offers a conditional expressions that begin with [[ and have a different format. Many like the [[ format better (it avoids, for example, the quote issue mentioned above) but the cost is loss of compatibility with other shells. In particular, note that dash, which is the default shell (/bin/sh) for scripts under Debian-derived distributions, does not have [[.
Bash thinks you're trying to assign a variable by saying a=3. You can do the following to fix this:
Use the = operator whilst referencing the variable with a $, like so: if [[ $a = 3 ]]
Use the -eq operator, which is special and doesn't require you to reference the variable with a $, but may not be compatible with all sh-derived shells: if [[ a -eq 3 ]]. If you wish to use -eq without Bash reference the variable: if [[ $a -eq 3 ]]
Note:
The double square brackets [[ ... ]] are a preferred format with specifically Bash conditionals. [ ... ] is good with any sh-derived shell (zsh, tcsh, etc).
if a=3 will assign value 3 to variable a
unless a is readonly variable, if a=3 always returns TRUE
same for if a=4
To compare variable a with a value, you can do this if [ $a = 3 ]
so the script should change to
#!/bin/bash
echo "Letter:"
read a
if [ $a = 3 ]
then
echo "LOL"
fi
if [ $a = 4 ]
then
echo "ROFL"
fi
Since a is read from user input, there is possibility user key in:
non numeric value
a string with empty space
nothing, user may just press Enter key
so a safer way to check is:
if [ "x$a" = "x3" ]
I couldn't find any one simple straightforward resource spelling out the meaning of and fix for the following BASH shell error, so I'm posting what I found after researching it.
The error:
-bash: [: too many arguments
Google-friendly version: bash open square bracket colon too many arguments.
Context: an if condition in single square brackets with a simple comparison operator like equals, greater than etc, for example:
VARIABLE=$(/some/command);
if [ $VARIABLE == 0 ]; then
# some action
fi
If your $VARIABLE is a string containing spaces or other special characters, and single square brackets are used (which is a shortcut for the test command), then the string may be split out into multiple words. Each of these is treated as a separate argument.
So that one variable is split out into many arguments:
VARIABLE=$(/some/command);
# returns "hello world"
if [ $VARIABLE == 0 ]; then
# fails as if you wrote:
# if [ hello world == 0 ]
fi
The same will be true for any function call that puts down a string containing spaces or other special characters.
Easy fix
Wrap the variable output in double quotes, forcing it to stay as one string (therefore one argument). For example,
VARIABLE=$(/some/command);
if [ "$VARIABLE" == 0 ]; then
# some action
fi
Simple as that. But skip to "Also beware..." below if you also can't guarantee your variable won't be an empty string, or a string that contains nothing but whitespace.
Or, an alternate fix is to use double square brackets (which is a shortcut for the new test command).
This exists only in bash (and apparently korn and zsh) however, and so may not be compatible with default shells called by /bin/sh etc.
This means on some systems, it might work from the console but not when called elsewhere, like from cron, depending on how everything is configured.
It would look like this:
VARIABLE=$(/some/command);
if [[ $VARIABLE == 0 ]]; then
# some action
fi
If your command contains double square brackets like this and you get errors in logs but it works from the console, try swapping out the [[ for an alternative suggested here, or, ensure that whatever runs your script uses a shell that supports [[ aka new test.
Also beware of the [: unary operator expected error
If you're seeing the "too many arguments" error, chances are you're getting a string from a function with unpredictable output. If it's also possible to get an empty string (or all whitespace string), this would be treated as zero arguments even with the above "quick fix", and would fail with [: unary operator expected
It's the same 'gotcha' if you're used to other languages - you don't expect the contents of a variable to be effectively printed into the code like this before it is evaluated.
Here's an example that prevents both the [: too many arguments and the [: unary operator expected errors: replacing the output with a default value if it is empty (in this example, 0), with double quotes wrapped around the whole thing:
VARIABLE=$(/some/command);
if [ "${VARIABLE:-0}" == 0 ]; then
# some action
fi
(here, the action will happen if $VARIABLE is 0, or empty. Naturally, you should change the 0 (the default value) to a different default value if different behaviour is wanted)
Final note: Since [ is a shortcut for test, all the above is also true for the error test: too many arguments (and also test: unary operator expected)
Just bumped into this post, by getting the same error, trying to test if two variables are both empty (or non-empty). That turns out to be a compound comparison - 7.3. Other Comparison Operators - Advanced Bash-Scripting Guide; and I thought I should note the following:
I used -e thinking it means "empty" at first; but that means "file exists" - use -z for testing empty variable (string)
String variables need to be quoted
For compound logical AND comparison, either:
use two tests and && them: [ ... ] && [ ... ]
or use the -a operator in a single test: [ ... -a ... ]
Here is a working command (searching through all txt files in a directory, and dumping those that grep finds contain both of two words):
find /usr/share/doc -name '*.txt' | while read file; do \
a1=$(grep -H "description" $file); \
a2=$(grep -H "changes" $file); \
[ ! -z "$a1" -a ! -z "$a2" ] && echo -e "$a1 \n $a2" ; \
done
Edit 12 Aug 2013: related problem note:
Note that when checking string equality with classic test (single square bracket [), you MUST have a space between the "is equal" operator, which in this case is a single "equals" = sign (although two equals' signs == seem to be accepted as equality operator too). Thus, this fails (silently):
$ if [ "1"=="" ] ; then echo A; else echo B; fi
A
$ if [ "1"="" ] ; then echo A; else echo B; fi
A
$ if [ "1"="" ] && [ "1"="1" ] ; then echo A; else echo B; fi
A
$ if [ "1"=="" ] && [ "1"=="1" ] ; then echo A; else echo B; fi
A
... but add the space - and all looks good:
$ if [ "1" = "" ] ; then echo A; else echo B; fi
B
$ if [ "1" == "" ] ; then echo A; else echo B; fi
B
$ if [ "1" = "" -a "1" = "1" ] ; then echo A; else echo B; fi
B
$ if [ "1" == "" -a "1" == "1" ] ; then echo A; else echo B; fi
B
Another scenario that you can get the [: too many arguments or [: a: binary operator expected errors is if you try to test for all arguments "$#"
if [ -z "$#" ]
then
echo "Argument required."
fi
It works correctly if you call foo.sh or foo.sh arg1. But if you pass multiple args like foo.sh arg1 arg2, you will get errors. This is because it's being expanded to [ -z arg1 arg2 ], which is not a valid syntax.
The correct way to check for existence of arguments is [ "$#" -eq 0 ]. ($# is the number of arguments).
I also faced same problem. #sdaau answer helped me in logical way. Here what I was doing which seems syntactically correct to me but getting too many arguments error.
Wrong Syntax:
if [ $Name != '' ] && [ $age != '' ] && [ $sex != '' ] && [ $birthyear != '' ] && [ $gender != '' ]
then
echo "$Name"
echo "$age"
echo "$sex"
echo "$birthyear"
echo "$gender"
else
echo "Enter all the values"
fi
in above if statement, if I pass the values of variable as mentioned below then also I was getting syntax error
export "Name"="John"
export "age"="31"
export "birthyear"="1990"
export "gender"="M"
With below syntax I am getting expected output.
Correct syntax:
if [ "$Name" != "" -a "$age" != "" -a "$sex" != "" -a "$birthyear" != "" -a "$gender" != "" ]
then
echo "$Name"
echo "$age"
echo "$sex"
echo "$birthyear"
echo "$gender"
else
echo "it failed"
fi
There are few points which we need to keep in mind
use "" instead of ''
use -a instead of &&
put space before and after operator sign like [ a = b], don't use as [ a=b ] in if condition
Hence above solution worked for me !!!
Some times If you touch the keyboard accidentally and removed a space.
if [ "$myvar" = "something"]; then
do something
fi
Will trigger this error message. Note the space before ']' is required.
I have had same problem with my scripts. But when I did some modifications it worked for me. I did like this :-
export k=$(date "+%k");
if [ $k -ge 16 ]
then exit 0;
else
echo "good job for nothing";
fi;
that way I resolved my problem. Hope that will help for you too.
Usually work in Windows, but trying to setup RabbitMQ on my Mac. Can someone let me know what the line below does?
[ "x" = "x$RABBITMQ_NODE_IP_ADDRESS" ] && [ "x" != "x$NODE_IP_ADDRESS" ] && RABBITMQ_NODE_IP_ADDRESS=${NODE_IP_ADDRESS}
Specifically, I'm curious about the [ "x" = "x$RAB..."] syntax.
If the RABBITMQ_NODE_IP_ADDRESS variable is empty/doesn't exist, it'll evaluate as "x" = "x" , which is true.
So it basically says, if RABBITMQ_NODE_IP_ADDRESS isn't set and NODE_IP_ADDRESS is set, set RABBITMQ_NODE_IP_ADDRESS=NODE_IP_ADDRESS
The "x" is used (somewhat superstitiously*) to prevent errors if the variable is null or unset. Most of the time the quotes take care of that for you. By putting the literal first and the variable second you eliminate errors in cases where the variable contains a string that starts with a dash, since test (aka [) would think it is an operator. In the case of your example, it would be preferable to use the -z and -n operators that test whether a variable is empty (null or unset) or not empty, respectively.
POSIX shells, such as Bourne (works in Bash, too):
[ -z $RABBITMQ_NODE_IP_ADDRESS ] && [ -n $NODE_IP_ADDRESS" ] && RABBITMQ_NODE_IP_ADDRESS=${NODE_IP_ADDRESS}
Bash (and ksh and zsh):
[[ -z $RABBITMQ_NODE_IP_ADDRESS && -n $NODE_IP_ADDRESS" ]] && RABBITMQ_NODE_IP_ADDRESS=${NODE_IP_ADDRESS}
* There may be some shells that need the "x", but some people do that "because it's always been done that way".
The "x" is not always superstitious, even in my relatively new bash (4.0.33).
Let's put the operation between parens. Empty variables are fine:
$ a=""
$ b=""
$ if [ '(' "$a" = "$b" ')' ]; then echo both_equal; fi
both_equal
But the ! operator for instance is not:
$ a='!'
$ if [ '(' "$a" = "$b" ')' ]; then echo both_equal; fi
bash: [: `)' expected, found
This is not a problem if we write "x$a" = "x$b" instead of "$a" = "$b".
The bracket [ is a test operator, which you can think of as an if statement. This is checking to see if the shell variable RABBITMQ_NODE_IP_ADDRESS is empty. Unfortunately, if you try to compare to an empty string "", the shell eliminates it before it does the test and your binary comparison operator only gets one (or maybe zero) operands. To prevent that error, it is a common practice to concatenate an "x" on each side of the =. Thus, instead of
[ "" = "<variable>" ]
becoming
[ = value ]
and yielding an error,
[ "X" = "X<variable>" ]
becomes
[ X = Xvalue ]
and the comparison may continue
A coworker claimed recently in a code review that the [[ ]] construct is to be preferred over [ ] in constructs like
if [ "`id -nu`" = "$someuser" ] ; then
echo "I love you madly, $someuser"
fi
He couldn't provide a rationale. Is there one?
[[ has fewer surprises and is generally safer to use. But it is not portable - POSIX doesn't specify what it does and only some shells support it (beside bash, I heard ksh supports it too). For example, you can do
[[ -e $b ]]
to test whether a file exists. But with [, you have to quote $b, because it splits the argument and expands things like "a*" (where [[ takes it literally). That has also to do with how [ can be an external program and receives its argument just normally like every other program (although it can also be a builtin, but then it still has not this special handling).
[[ also has some other nice features, like regular expression matching with =~ along with operators like they are known in C-like languages. Here is a good page about it: What is the difference between test, [ and [[ ? and Bash Tests
Behavior differences
Some differences on Bash 4.3.11:
POSIX vs Bash extension:
[ is POSIX
[[ is a Bash extension inspired from KornShell
regular command vs magic
[ is just a regular command with a weird name.
] is just the last argument of [.
Ubuntu 16.04 actually has an executable for it at /usr/bin/[ provided by coreutils, but the Bash built-in version takes precedence.
Nothing is altered in the way that Bash parses the command.
In particular, < is redirection, && and || concatenate multiple commands, ( ) generates subshells unless escaped by \, and word expansion happens as usual.
[[ X ]] is a single construct that makes X be parsed magically. <, &&, || and () are treated specially, and word splitting rules are different.
There are also further differences like = and =~.
In Bashese: [ is a built-in command, and [[ is a keyword: What's the difference between shell builtin and shell keyword?
<
[[ a < b ]]: lexicographical comparison
[ a \< b ]: Same as above. \ required or else does redirection like for any other command. Bash extension.
expr x"$x" \< x"$y" > /dev/null or [ "$(expr x"$x" \< x"$y")" = 1 ]: POSIX equivalents, see: How to test strings for lexicographic less than or equal in Bash?
&& and ||
[[ a = a && b = b ]]: true, logical and
[ a = a && b = b ]: syntax error, && parsed as an AND command separator cmd1 && cmd2
[ a = a ] && [ b = b ]: POSIX reliable equivalent
[ a = a -a b = b ]: almost equivalent, but deprecated by POSIX because it is insane and fails for some values of a or b like ! or ( which would be interpreted as logical operations
(
[[ (a = a || a = b) && a = b ]]: false. Without ( ) it would be true, because [[ && ]] has greater precedence than [[ || ]]
[ ( a = a ) ]: syntax error, () is interpreted as a subshell
[ \( a = a -o a = b \) -a a = b ]: equivalent, but (), -a, and -o are deprecated by POSIX. Without \( \) it would be true, because -a has greater precedence than -o
{ [ a = a ] || [ a = b ]; } && [ a = b ] non-deprecated POSIX equivalent. In this particular case however, we could have written just: [ a = a ] || [ a = b ] && [ a = b ], because the || and && shell operators have equal precedence, unlike [[ || ]] and [[ && ]] and -o, -a and [
word splitting and filename generation upon expansions (split+glob)
x='a b'; [[ $x = 'a b' ]]: true. Quotes are not needed
x='a b'; [ $x = 'a b' ]: syntax error. It expands to [ a b = 'a b' ]
x='*'; [ $x = 'a b' ]: syntax error if there's more than one file in the current directory.
x='a b'; [ "$x" = 'a b' ]: POSIX equivalent
=
[[ ab = a? ]]: true, because it does pattern matching (* ? [ are magic). Does not glob expand to files in the current directory.
[ ab = a? ]: a? glob expands. So it may be true or false depending on the files in the current directory.
[ ab = a\? ]: false, not glob expansion
= and == are the same in both [ and [[, but == is a Bash extension.
case ab in (a?) echo match; esac: POSIX equivalent
[[ ab =~ 'ab?' ]]: false, loses magic with '' in Bash 3.2 and above and provided compatibility to Bash 3.1 is not enabled (like with BASH_COMPAT=3.1)
[[ ab? =~ 'ab?' ]]: true
=~
[[ ab =~ ab? ]]: true. POSIX extended regular expression match and ? does not glob expand
[ a =~ a ]: syntax error. No Bash equivalent.
printf 'ab\n' | grep -Eq 'ab?': POSIX equivalent (single-line data only)
awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': POSIX equivalent.
Recommendation: always use []
There are POSIX equivalents for every [[ ]] construct I've seen.
If you use [[ ]] you:
lose portability
force the reader to learn the intricacies of another Bash extension. [ is just a regular command with a weird name, and no special semantics are involved.
Thanks to Stéphane Chazelas for important corrections and additions.
[[ ]] has more features - I suggest you take a look at the Advanced Bash Scripting Guide for more information, specifically the extended test command section in Chapter 7. Tests.
Incidentally, as the guide notes, [[ ]] was introduced in ksh88 (the 1988 version of KornShell).
From Which comparator, test, bracket, or double bracket, is fastest?:
The double bracket is a “compound
command” where as test and the single
bracket are shell built-ins (and in
actuality are the same command). Thus,
the single bracket and double bracket
execute different code.
The test and single bracket are the
most portable as they exist as
separate and external commands.
However, if your using any remotely
modern version of BASH, the double
bracket is supported.
If you are into following Google's style guide:
Test, [ … ], and [[ … ]]
[[ … ]] is preferred over [ … ], test and /usr/bin/[.
[[ … ]] reduces errors as no pathname expansion or word splitting takes place between [[ and ]]. In addition, [[ … ]] allows for regular expression matching, while [ … ] does not.
# This ensures the string on the left is made up of characters in
# the alnum character class followed by the string name.
# Note that the RHS should not be quoted here.
if [[ "filename" =~ ^[[:alnum:]]+name ]]; then
echo "Match"
fi
# This matches the exact pattern "f*" (Does not match in this case)
if [[ "filename" == "f*" ]]; then
echo "Match"
fi
# This gives a "too many arguments" error as f* is expanded to the
# contents of the current directory
if [ "filename" == f* ]; then
echo "Match"
fi
For the gory details, see E14 at http://tiswww.case.edu/php/chet/bash/FAQ
In a question tagged 'bash' that explicitly has "in Bash" in the title, I'm a little surprised by all of the replies saying you should avoid [[...]] because it only works in bash!
It's true that portability is the primary objection: if you want to write a shell script which works in Bourne-compatible shells even if they aren't bash, you should avoid [[...]]. (And if you want to test your shell scripts in a more strictly POSIX shell, I recommend dash; though it is an incomplete POSIX implementation since it lacks the internationalization support required by the standard, it also lacks support for most, but not all, of the many non-POSIX constructs found in bash, ksh, zsh, etc.)
The other objection I see is at least applicable within the assumption of bash: that [[...]] has its own special rules which you have to learn, while [...] acts like just another command. That is again true (and Mr. Santilli brought the receipts showing all the differences), but it's rather subjective whether the differences are good or bad. I personally find it freeing that the double-bracket construct lets me use (...) for grouping, && and || for Boolean logic, < and > for comparison, and unquoted parameter expansions. It's like its own little closed-off world where expressions work more like they do in traditional, non-command-shell programming languages.
A point I haven't seen raised is that this behavior of [[...]] is entirely consistent with that of the arithmetic expansion construct $((...)), which is specified by POSIX, and also allows unquoted parentheses and Boolean and inequality operators (which here perform numeric instead of lexical comparisons). Essentially, any time you see the doubled bracket characters you get the same quote-shielding effect.
(Bash and its modern relatives also use ((...)) – without the leading $ – as either a C-style for loop header or an environment for performing arithmetic operations; neither syntax is part of POSIX.)
So there are some good reasons to prefer [[...]]; there are also reasons to avoid it, which may or may not be applicable in your environment. As to your coworker, "our style guide says so" is a valid justification, as far as it goes, but I'd also seek out backstory from someone who understands why the style guide recommends what it does.
A typical situation where you cannot use [[ is in an autotools configure.ac script. There brackets have a special and different meaning, so you will have to use test instead of [ or [[ -- Note that test and [ are the same program.
I am surprised that I haven't seen this issue raised sooner, but consider:
$ n=5
$ [[ $n -gt 0 ]] && echo $n is positive
5 is positive
$ n=foo
$ [[ $n -gt 0 ]] && echo $n is positive
$ [ "$n" -gt 0 ] && echo $n is positive
bash: [: foo: integer expression expected
The lack of error message from the line [[ $n -gt 0 ]] renders [[ completely unusable. Perhaps I'm being too harsh and the correct response is to simply avoid integer comparisons in [[, but I strongly advise against [[ for this reason.
In a nutshell, [[ is better because it doesn't fork another process. No brackets or a single bracket is slower than a double bracket because it forks another process.
[[ ]] double brackets are unsupported under certain versions of SunOS and totally unsupported inside function declarations by:
GNU Bash, version 2.02.0(1)-release (sparc-sun-solaris2.6)