809

I'm trying to get an if statement to work in Bash (using Ubuntu):

#!/bin/bash

s1="hi"
s2="hi"

if ["$s1" == "$s2"]
then
  echo match
fi

I've tried various forms of the if statement, using [["$s1" == "$s2"]], with and without quotes, using =, == and -eq, but I still get the following error:

[hi: command not found

I've looked at various sites and tutorials and copied those, but it doesn't work - what am I doing wrong?

Eventually, I want to say if $s1 contains $s2, so how can I do that?

I did just work out the spaces bit... :/ How do I say contains?

I tried

if [[ "$s1" == "*$s2*" ]]

but it didn't work.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mr Shoubs
  • 14,629
  • 17
  • 68
  • 107
  • See also https://stackoverflow.com/questions/9581064/why-should-be-there-a-space-after-and-before-in-the-bash-script – tripleee Dec 15 '15 at 09:11

12 Answers12

1144

For string equality comparison, use:

if [[ "$s1" == "$s2" ]]

For string does NOT equal comparison, use:

if [[ "$s1" != "$s2" ]]

For the a contains b, use:

if [[ $s1 == *"$s2"* ]]

(and make sure to add spaces between the symbols):

Bad:

if [["$s1" == "$s2"]]

Good:

if [[ "$s1" == "$s2" ]]
Basil Musa
  • 8,198
  • 6
  • 64
  • 63
moinudin
  • 134,091
  • 45
  • 190
  • 216
  • 4
    http://stackoverflow.com/a/229606/376454 I had to use this answer to compare a variable to a fixed string. – Wok Feb 13 '14 at 13:06
  • 3
    The picky guys on IRC are telling me you should use if [[ "$s1" == "$s2" ]] or case. – László Papp Oct 20 '14 at 17:24
  • 21
    The double equals sign is an error in the first case. Bash tolerates it, but the portable variant is `if [ "$s1" = "$s2" ]`. See also [Rahul's answer](http://stackoverflow.com/a/36279399/874188) – tripleee Aug 12 '16 at 04:59
  • [[ does not work on all shells unfortunately. On Travis CI, it gives : `[[: not found` – Cyan Dec 06 '16 at 19:54
  • 4
    Hi, I wonder why this is bad -> if ["$s1" == "$s2"] what's the point with the spaces ? – Sangimed Jun 15 '17 at 12:35
  • 3
    POSIX `test` only specifies `=` as a string comparison operator. `==` is a nonportable extension. Thus, it's a better habit to use `[ "$str1" = "$str2" ]` rather than `[ "$str1" == "$str2" ]` (which won't work on baseline-POSIX shells such as dash). – Charles Duffy Jan 27 '18 at 23:00
  • 38
    @Sangimed, `[` is a **command** (actually, an alternate name for the command called `test`); if you run `which [`, you'll see there's actually an executable file for it on disk (even though the shell may provide a built-in implementation as a performance optimization). Just like you have to put a space between the name of the command `ls` before the name of the file you want it to print, you need to put a space after the name of the `[` command and its first argument, and between each argument it's passed (if invoked as `[` rather than `test`, it expects its last argument to be `]`). – Charles Duffy Jan 27 '18 at 23:01
  • nice work...... – kta May 18 '20 at 06:13
  • @Sangimed, that is **so** helpful! I've been wondering about that for years! I've been running `man test` for a long time, but I just ran `man [` and it works too! I had NO IDEA! – Gabriel Staples Feb 15 '21 at 01:53
  • 1
    Probably the best example of a good answer on all of Stack Overflow as per my opinion. To the point yet covers the broader picture and even addresses the most probable questions from the reader. – Nikita Volkov Jun 16 '22 at 22:10
204

You should be careful to leave a space between the sign of '[' and double quotes where the variable contains this:

if [ "$s1" == "$s2" ]; then
#   ^     ^  ^     ^
   echo match
fi

The ^s show the blank spaces you need to leave.

Robin Daugherty
  • 7,115
  • 4
  • 45
  • 59
trejo08
  • 2,348
  • 1
  • 13
  • 12
189

You need spaces:

if [ "$s1" == "$s2" ]
unwind
  • 391,730
  • 64
  • 469
  • 606
43

I suggest this one:

if [ "$a" = "$b" ]

Notice the white space between the openning/closing brackets and the variables and also the white spaces wrapping the '=' sign.

Also, be careful of your script header. It's not the same thing whether you use

#!/bin/bash

or

#!/bin/sh

Here's the source.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Abderrazak BOUADMA
  • 1,526
  • 2
  • 16
  • 38
40

Bash 4+ examples. Note: not using quotes will cause issues when words contain spaces, etc. Always quote in Bash IMO.

Here are some examples Bash 4+:

Example 1, check for 'yes' in string (case insensitive):

if [[ "${str,,}" == *"yes"* ]] ;then

Example 2, check for 'yes' in string (case insensitive):

if [[ "$(echo "$str" | tr '[:upper:]' '[:lower:]')" == *"yes"* ]] ;then

Example 3, check for 'yes' in string (case sensitive):

 if [[ "${str}" == *"yes"* ]] ;then

Example 4, check for 'yes' in string (case sensitive):

 if [[ "${str}" =~ "yes" ]] ;then

Example 5, exact match (case sensitive):

 if [[ "${str}" == "yes" ]] ;then

Example 6, exact match (case insensitive):

 if [[ "${str,,}" == "yes" ]] ;then

Example 7, exact match:

 if [ "$a" = "$b" ] ;then
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mike Q
  • 6,716
  • 5
  • 55
  • 62
29

This question has already great answers, but here it appears that there is a slight confusion between using single equal (=) and double equals (==) in

if [ "$s1" == "$s2" ]

The main difference lies in which scripting language you are using. If you are using Bash then include #!/bin/bash in the starting of the script and save your script as filename.bash. To execute, use bash filename.bash - then you have to use ==.

If you are using sh then use #!/bin/sh and save your script as filename.sh. To execute use sh filename.sh - then you have to use single =. Avoid intermixing them.

Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
does_it_matter
  • 570
  • 6
  • 15
  • 7
    The assertion "you have to use `==`" is incorrect. Bash supports both `=` and `==`. Also, if you have `#!/bin/bash` at the start of your script, you can make it executable and run it like `./filename.bash` (not that the file extension is important). – Tom Fenech Jul 15 '16 at 08:27
  • Perfect, I think I have to delete this answer now but it will be very helpful if you explain why this is not working without making the file executable and running by adding sh/bash before the filename? – does_it_matter Jul 18 '16 at 16:47
  • This is confused about the significance of the shebang and the file name. If you have correctly put `#!/bin/sh` or `#!/bin/bash` as the first line of the script, you simply run it with `./filename` and the actual file name can be completely arbitrary. – tripleee Aug 12 '16 at 05:02
  • Now this is getting interesting,even if you don't add any shebang and any extension and execute it using "bash/sh filename" it is working no matter what you use single equal or double equals.One more thing if you make the same file(without shebang and any extension) executable then you can execute it like ./filename (no matter of single or double equals).(Tried it on Arch linux with bash 4.3.46). – does_it_matter Aug 12 '16 at 17:08
  • If you execute the file by running say "bash filename" - then you are just passing 'filename' as a parameter to the program 'bash' - which of course will result in bash running it. However if you set 'filename' execute permission, and try to run it by eg './filename' - then you are relying on the default 'execute' behaviour of your current command shell - which probably requires the "#!(shell)" line at the start of the script, in order to work. – MikeW Sep 23 '16 at 10:15
  • It's falso to say that you "have to use" `==` in bash -- bash supports both the POSIX syntax and the extended syntax, so `=` works in both places, whereas `==` works *only* in extended shells. – Charles Duffy Jan 27 '18 at 23:07
19

I would suggest:

#!/bin/bash

s1="hi"
s2="hi"

if [ $s1 = $s2 ]
then
  echo match
fi

Without the double quotes and with only one equals.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
jzrk
  • 285
  • 1
  • 2
  • Yes that's true, I missed the spaces. With "[ $s1 = $s2 ]" it works. – jzrk Nov 25 '10 at 13:51
  • 7
    Why would you omit the double quotes? They are optional but harmless in this limited specific case, but removing them would be a serious bug in many real-world situations. See also http://stackoverflow.com/questions/10067266/when-to-wrap-quotes-around-a-variable – tripleee Feb 06 '16 at 10:53
  • 4
    Try with `s1='*'` and `s2='*'`, and you'll see that leaving out the double quotes is a serious mistake. – Charles Duffy Jan 27 '18 at 23:06
  • I got same behavior with = or == regardless of quotes. I'm not sure if this is shell specific, I am using zsh on my mac OS version : 10.15.7 (19H15) – Badri Paudel Apr 10 '22 at 12:02
13
$ if [ "$s1" == "$s2" ]; then echo match; fi
match
$ test "s1" = "s2" ;echo match
match
$
qwerty
  • 3,801
  • 2
  • 28
  • 43
  • 5
    The double equals sign is tolerated in Bash, but not in some other dialects. For portability, the single equals sign should be preferred, and if you target Bash only, the double brackets `[[` extension would be superior for versatility, robustness, and convenience. – tripleee Feb 06 '16 at 10:56
8

I don't have access to a Linux box right now, but [ is actually a program (and a Bash builtin), so I think you have to put a space between [ and the first parameter.

Also note that the string equality operator seems to be a single =.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mrki
  • 368
  • 2
  • 7
  • The symbol [ was a link to /bin/test at one time (or perhaps vice versa). That is apparently no longer the case on ubuntu 16.04; no idea where or when the change occurred. – Prisoner 13 Oct 10 '17 at 00:52
  • I believe Prisoner 13 was incorrect. '[' **is** equivalent to 'test' in Ubuntu v20, with the requirement that the last argument must be ']'. – TonyG Sep 10 '22 at 19:47
7

This is more a clarification than an answer! Yes, the clue is in the error message:

[hi: command not found

which shows you that your "hi" has been concatenated to the "[".

Unlike in more traditional programming languages, in Bash, "[" is a command just like the more obvious "ls", etc. - it's not treated specially just because it's a symbol, hence the "[" and the (substituted) "$s1" which are immediately next to each other in your question, are joined (as is correct for Bash), and it then tries to find a command in that position: [hi - which is unknown to Bash.

In C and some other languages, the "[" would be seen as a different "character class" and would be disjoint from the following "hi".

Hence you require a space after the opening "[".

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
MikeW
  • 5,504
  • 1
  • 34
  • 29
5

Use:

#!/bin/bash

s1="hi"
s2="hi"

if [ "x$s1" == "x$s2" ]
then
  echo match
fi

Adding an additional string inside makes it more safe.

You could also use another notation for single-line commands:

[ "x$s1" == "x$s2" ] && echo match
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
mpapis
  • 52,729
  • 14
  • 121
  • 158
  • 4
    What does it mean "more safe"? It is important to explain any such qualification, for sake of completeness and clarity. – mloskot Jul 09 '13 at 21:53
  • 4
    the truth it's not safer, now I know that would be safer if you would not quote it and this way prevent syntax error if one of them was empty – mpapis Jul 10 '13 at 01:26
3

For a version with pure Bash and without test, but really ugly, try:

if ( exit "${s1/*$s2*/0}" )2>/dev/null
then
   echo match
fi

Explanation: In ( )an extra subshell is opened. It exits with 0 if there was a match, and it tries to exit with $s1 if there was no match which raises an error (ugly). This error is directed to /dev/null.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user2546460
  • 1,223
  • 2
  • 8
  • 8
  • Not bad at all. Like the explanation and the sed regexp like. I never need to use subshell that way. As i know you can get the output with `command` or $(command). Im sure you can TEST it then make it better. – m3nda Apr 18 '15 at 19:49
  • 3
    This is trivially broken in the case where `s2` contains globs: to fix this, you need to quote the expansion `$s2`: `"${s1/*"$s2"*/0}"`. But there are other subtle bugs that are impossible to fix: e.g., if `s1` is a list of `0`'s: `s1=000000; s2=some_other_stuff` will claim a match. So I would highly recommend against using this method! Another bug: `s1=--; s2=stuff`. Starting from bash 4.4, `s1=--help; s2=stuff` would also spam standard output. – gniourf_gniourf Jun 17 '16 at 15:10