I've been trying to compare whether two numbers in Bash are equal (and print a message if they are equal), but I'm getting some strange error messages for this simple program:
#!/bin/bash
fun2 (){
$x = 3
//#prog.sh: line 4: =: command not found
if [x == 3]
then
//#prog.sh: line 6: [x: command not found
echo "It's 3!"
fi
}
fun2
The corresponding errors are shown below the lines that caused those errors.
It must be:
if [ $x -eq 3 ]; then .....
If you prefer a more readable and self-explanatory code, use this syntax:
if test $x -eq 3; then .....
Explanation:
To compare integers you must use those operators (copied from man test):
INTEGER1 -eq INTEGER2
INTEGER1 is equal to INTEGER2
INTEGER1 -ge INTEGER2
INTEGER1 is greater than or equal to INTEGER2
INTEGER1 -gt INTEGER2
INTEGER1 is greater than INTEGER2
INTEGER1 -le INTEGER2
INTEGER1 is less than or equal to INTEGER2
INTEGER1 -lt INTEGER2
INTEGER1 is less than INTEGER2
INTEGER1 -ne INTEGER2
INTEGER1 is not equal to INTEGER2
operators == and != are for string comparison only.
For information: "[" command is an alias for system "test" command.
For the first error message, remove the dollar sign $ and no spaces are allowed around the equal sign =
x=3
For the second error message, insert a space and a dollar sign $ before x and a space after 3
if [ $x -eq 3 ]
As loentar correctly pointed out, it must be -eq and neither = nor ==.
Shell scripting wasn't really meant to be a full language, and it is heavily dependent upon other external commands to work.
Variables use a sigil which is that $ in front of them. A lot of shell scripting languages use variable sigils to help distinguish between a string and a variable.
For example:
foo=foo_value
echo foo foo $foo foo
Will print:
foo foo foo_value foo
Note that quotes are not necessary for the echo statement for strings. Windows Batch shell is very similar:
set foo = foo_value
echo foo foo %foo% foo
As you can see, the sigil is used when the variable is supposed to be expanded, but not when you define it. That's because Unix shells are intelligent shells. They munge the command line before it is even executed. The shell will substitute the environment variables before execution:
foo=bar
$foo="whose value is this" # Note the dollar sign!
echo The value of foo is $foo
echo The value of bar is $bar
This will print out:
The value of foo is foo
The value of bar is whose value is this
If you use the set -xv command, you'll see that $foo="whose value is this" is expanded to bar=whose value is this" before it is executed.
In Bourne style shells like KornShell and Bash, the if statement isn't what you think it is. The if command executes the statement, and will select the if clause if that command returns a zero value. For example:
cat "foo" > my_file # Create a one line file with the string foo in it.
if grep -q "foo" my_file # grep command will return a zero exit code if it finds foo
then
echo "The string 'foo' is in file my_file"
fi
Notice that the if clause isn't a Boolean statement. It's an actual command that is executed.
Somewhere early in Unix development, the test command was created. You can do a man test and see how to use it.
The test command allows you to do this:
foo=3
bar=3
if test foo -eq bar
then
echo "foo and bar are equal"
else
echo "foo and bar are not equal"
fi
If you do this:
$ ls -li /bin/test /bin/[
You will see that a command called [ actually exists, and is a hard link to the test command. This was created to make an if statement look more like a if statement you'll see in regular programming languages:
foo=3
bar=3
if [ foo -eq bar ]
then
echo "foo and bar are equal"
else
echo "foo and bar are not equal"
fi
It is the exact same script as above, but with [ instead of test.
This explains why you need the dash in many tests (it's a parameter to the test command, and parameters start with a dash!). It also explains why you need spaces around the [ and ]. These are actual Unix commands, and Unix commands must have white spaces around them, so the shell can process them.
Another issue is: Why does the shell have two different sets of tests for strings and numbers? That's because strings may contain nothing but digits, but are not really numeric. For example, if you do inventory, you might have a part number 001 and 01. Numerically, they're equal, but as strings, they're two different strings. There is no way for the shell script to know. Instead, you must let the shell script know if it's a numeric comparison or a string comparison.
Perl has similar issues since you don't declare variables as numeric or non-numeric:
Shell Script Perl
Boolean Operator Numeric String Numeric String
=================== ======= ====== ======= ======
Equals -eq = == eq
Not Equals -ne != != ne
Greater Than -gt > > gt
Less Than -lt < < lt
Greater or Equals -ge >= >= ge
Less Than or Equals -le <= <= le
You can try a few other things:
$ echo "*" # Echos the asterisk
$ echo * # No quotes: Prints all files in current directory
Notice again the shell expands the * before executing the echo command. This is the main difference between a shell script and a typical programming language. The shell first does expansion (fill in environment variables, glob substitution, and run sub-commands) before it actually executes the command.
The set -xv will show you what command is being executed, and how the shell expands the command before executing. Doing set +xv will shut that off. Play around with that, and you'll soon understand the shell a bit better.
the spaces are mandatory in the [ ] expression, so
if [ $x == 3 ]; then
you need a sigill on variables in the [ ] tests
For clarity, use == for equality rather than = even though both work. The former encourages the use of [[ and the latter can be confused with an assignment. Use (( … )) or -lt and -gt for numerical comparison.
if [[ "${my_var}" == "val" ]]; then
do_something
fi
if (( my_var == 3 )); then
do_something
fi
if [[ "${my_var}" -gt 3 ]]; then
do_something
fi
Related
As far as I had known, [[ and [ can be expected to behave mostly the same, taking into account a few extra features [[ has. But recently I noticed a discrepancy in how bash treats octal expansions:
$ b=010; echo $((b))
8
$ [[ $b -eq 8 ]]; echo $?
0
but
$ [ $b -eq 8 ]; echo $?
1
$ test $b -eq 8; echo $?
1
$ [ $b -eq 10 ]; echo $?
0
Why does the latter expression drop the auto octal conversion? Expressions like -eq are "Arithmetic" according to help test in Bash and the Bash Reference Manual, and further according to the next section of the reference manual constants with a leading zero can be treated as octal.
POSIX sh is a little less clear on the subject: Even though POSIX arithmetic expressions still expand leading-zero integers to their octal value, it refers to -eq expressions in test as algebraic, not arithmetic.
Is there any documentation or evidence to suggest that bash makes a distinction between [[ and [ for octal expansion on purpose, or is it just an accidental feature?
[[ is known as the extended test command and behaves as ksh88, you can find an elaboration here:
http://tldp.org/LDP/abs/html/testconstructs.html#DBLBRACKETS
Also if you need to be sure of the base in bash you can use the # base operator like this:
b=010; echo $((10#$b))
10
b=10; echo $((8#$b)
8
b=013; echo $((8#$b))
11
Can anyone see what I did wrong here? I keep getting the following error message: [[: not found
read INPUT
if [[ "$INPUT" -ge 1 ]] && [[ "$INPUT" -le 10 ]]; then
Do something
else
printf "Please enter a value between 1 and 10"
fi
[[ is not available in scripts which start with #!/bin/sh, or which are started with sh yourscript. Start your script with #!/bin/bash if you want to use it.
See also http://mywiki.wooledge.org/BashGuide/Practices#Choose_Your_Shell
If you are going to use bash, by the way, there's a better syntax for numeric comparisons:
if (( input >= 1 && input <= 10 )); then ...
Note that lower-case variable names are preferred for local use -- all-upper-case names are reserved for environment variables and shell builtins.
If you're not going to use bash, use the POSIX test operator:
if [ "$input" -ge 1 ] && [ "$input" -le 10 ]; then ...
Note that when using [ ] correct quoting is essential, whereas with [[ ]] it is often superfluous; also, [ ] is missing some extensions such as pattern-matching and regular-expression operators.
It's complicated:
First, there are three separate ways of constructing your if statement. Each way has its own unique syntax on how to join two booleans. (Actually, there are four ways since one way allows you to use list operators).
A little background...
The if command is a compound command built into the shell. The if command executes the commands following the if. If that command returns a zero value, the if statement is considered true and the then clause executes. Otherwise, if it exists, the else clause will execute. Remember, the if is just a command. You can do things like this:
if ! mv "$foo" "$bar"
then
echo "I can't move $foo to $bar"
exit 2
fi
What we need is a command to do some testing for us. If the test succeeds, that test command returns an exit code of zero. If not, it returns a non-zero exit code. Then, it could be used with the if command!
The test command (Yes, there's really one!).
The [ is an alias for the test command which was created to allow you to test files, strings, and numbers for the if statement. (This is now a built in command in Bash, but its roots are actually part of /bin/test and /bin/[). These are the same:
if test "$foo" -eq "$bar"
then
...
fi
and
if [ "$foo" -eq "$bar" ]
then
...
fi
The test command (if you read the manpage has a -a And test and a -o Or test. You could have done:
if [ "$INPUT" -ge 1 -a "$INPUT" -le 10 ]
then
....
fi
This is a single test statement with three test parameters (-ge, -a, and -le).
Using List Operators
This isn't the only way to do a compound boolean test. The Bash shell has two list operators: && and ||. The list operators go in between two commands. If you use && and the left hand command returns a non-zero exit code, the right hand command is not executed, and the entire list returns the exit value of the left-hand command. If you use ||, and the left hand command succeeds, the right hand command is not executed, and the entire list returns a zero exit value. If the first command returns a non-zero exit value, the right-hand command is executed, and the entire list returns the exit value of the right-hand command.
That's why you can do things like this:
[ $bar -eq 0 ] || echo "Bar doesn't have a zero value"!
Since [ ... ] is just a command that returns a zero or non-zero value, we can use these list operators as part of our test:
if [ "$INPUT" -ge 1 ] && [ "$INPUT" -le 10 ]
then
...
fi
Note that this is two separate tests and are separated by a && list operator.
Bash's Special [[ compound command
In Kornshell, Zsh, and Bash, there are special compound commands for testing. These are the double square brackets. They appear to be just like the single square brackets command, but because they're compound commands, parsing is affected.
For example:
foo="This has white space"
bar="" #No value
if [ ! $foo = $bar ] # Doesn't work!
then
The shell expands $foo and $bar and the test will become:
if [ This has white space = ]
which just doesn't work. However,
if [[ $foo != $bar ]]
works fine because of special parsing rules. The double brackets allow you to use parentheses for grouping and && and || as boolean operators. Thus:
if [[ $INPUT -ge 1 && $INPUT -le 10 ]]
then
...
fi
Note that the && appears inside a single set of double square brackets. (Note there's no need for quotation marks)
Mathematical Boolean Expression
Bash has built in mathematical processing including mathematical boolean expressions. If you put something between double parentheses, Bash will evaluate it mathematically:
if (( $INPUT >= 1 && $INPUT <= 10 ))
then
...
fi
In this case, (( $INPUT >= 1 && $INPUT <= 10 )) is evaluated. If $INPUT is between 1 and 10 inclusively, the mathematical expression will evaluate as true (zero exit code), and thus the then clause will be executed.
So, you can:
Use the original test (single square brackets) command and use the -a to string together two boolean statements in a single test.
Use list operators to string together two separate test commands (single square brackets).
Use the newer compound test command (double square brackets) that now include && and || as boolean operators, so you have a single compound test.
Forget about test command and just use mathematical evaluation (double parentheses) to evaluate boolean expressions.
Test Constructs Can Vary by Shell
As has been mentioned in other posts, [[ is a Bash shell keyword that isn't present in the Bourne shell. You can see this from a Bash prompt with:
type '[['
[[ is a shell keyword
In a Bourne shell, you will instead get "command not found."
Be More Portable: Use the -a Test Operator
A more portable construct is to use the -a test operator to join conditions (see man test for details). For example:
if [ "$INPUT" -ge 1 -a "$INPUT" -le 10 ]; then
: # do something when both conditions are true
else
: # do something when either condition is false
fi
This will work in every Bourne-compatible shell I've ever used, and on any system that has a /bin/\[ executable.
I'm just starting out in Bash scripting, and I can't seem to work out what these arguments mean (-ne and -n). Are they Bash arguments or self created arguments?
# Run as root
if [ "$UID" -ne "$ROOT_UID" ]
then
echo "This must be run as root"
exit $E_NOTROOT
fi
if [ -n "S1" ]
then
The mnemonic for -ne is 'not equal'; it does an arithmetic comparison on the two values for inequality.
The mnemonic for -n is 'not empty'; it tests whether the argument ("$1" in this case) is an empty string. If $1 is defined and has a value other than the empty string, the test will be true.
See Bash conditional expressions for more details.
The test command, also known as [, supports the other numeric comparison operators too: -lt (less than), -le (less than or equal to), -gt (greater than), -ge (greater than or equal to), and -eq (equal). The -z operator tests for a zero length string — but note that the argument must be enclosed in double quotes, though ([ -z "$variable" ]) as otherwise there is no argument for -z to test.
There are many other test operators; this is not an exhaustive list.
from man test :
-n STRING
the length of STRING is nonzero
INTEGER1 -ne INTEGER2
INTEGER1 is not equal to INTEGER2
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've been trying to compare whether two numbers in Bash are equal (and print a message if they are equal), but I'm getting some strange error messages for this simple program:
#!/bin/bash
fun2 (){
$x = 3
//#prog.sh: line 4: =: command not found
if [x == 3]
then
//#prog.sh: line 6: [x: command not found
echo "It's 3!"
fi
}
fun2
The corresponding errors are shown below the lines that caused those errors.
It must be:
if [ $x -eq 3 ]; then .....
If you prefer a more readable and self-explanatory code, use this syntax:
if test $x -eq 3; then .....
Explanation:
To compare integers you must use those operators (copied from man test):
INTEGER1 -eq INTEGER2
INTEGER1 is equal to INTEGER2
INTEGER1 -ge INTEGER2
INTEGER1 is greater than or equal to INTEGER2
INTEGER1 -gt INTEGER2
INTEGER1 is greater than INTEGER2
INTEGER1 -le INTEGER2
INTEGER1 is less than or equal to INTEGER2
INTEGER1 -lt INTEGER2
INTEGER1 is less than INTEGER2
INTEGER1 -ne INTEGER2
INTEGER1 is not equal to INTEGER2
operators == and != are for string comparison only.
For information: "[" command is an alias for system "test" command.
For the first error message, remove the dollar sign $ and no spaces are allowed around the equal sign =
x=3
For the second error message, insert a space and a dollar sign $ before x and a space after 3
if [ $x -eq 3 ]
As loentar correctly pointed out, it must be -eq and neither = nor ==.
Shell scripting wasn't really meant to be a full language, and it is heavily dependent upon other external commands to work.
Variables use a sigil which is that $ in front of them. A lot of shell scripting languages use variable sigils to help distinguish between a string and a variable.
For example:
foo=foo_value
echo foo foo $foo foo
Will print:
foo foo foo_value foo
Note that quotes are not necessary for the echo statement for strings. Windows Batch shell is very similar:
set foo = foo_value
echo foo foo %foo% foo
As you can see, the sigil is used when the variable is supposed to be expanded, but not when you define it. That's because Unix shells are intelligent shells. They munge the command line before it is even executed. The shell will substitute the environment variables before execution:
foo=bar
$foo="whose value is this" # Note the dollar sign!
echo The value of foo is $foo
echo The value of bar is $bar
This will print out:
The value of foo is foo
The value of bar is whose value is this
If you use the set -xv command, you'll see that $foo="whose value is this" is expanded to bar=whose value is this" before it is executed.
In Bourne style shells like KornShell and Bash, the if statement isn't what you think it is. The if command executes the statement, and will select the if clause if that command returns a zero value. For example:
cat "foo" > my_file # Create a one line file with the string foo in it.
if grep -q "foo" my_file # grep command will return a zero exit code if it finds foo
then
echo "The string 'foo' is in file my_file"
fi
Notice that the if clause isn't a Boolean statement. It's an actual command that is executed.
Somewhere early in Unix development, the test command was created. You can do a man test and see how to use it.
The test command allows you to do this:
foo=3
bar=3
if test foo -eq bar
then
echo "foo and bar are equal"
else
echo "foo and bar are not equal"
fi
If you do this:
$ ls -li /bin/test /bin/[
You will see that a command called [ actually exists, and is a hard link to the test command. This was created to make an if statement look more like a if statement you'll see in regular programming languages:
foo=3
bar=3
if [ foo -eq bar ]
then
echo "foo and bar are equal"
else
echo "foo and bar are not equal"
fi
It is the exact same script as above, but with [ instead of test.
This explains why you need the dash in many tests (it's a parameter to the test command, and parameters start with a dash!). It also explains why you need spaces around the [ and ]. These are actual Unix commands, and Unix commands must have white spaces around them, so the shell can process them.
Another issue is: Why does the shell have two different sets of tests for strings and numbers? That's because strings may contain nothing but digits, but are not really numeric. For example, if you do inventory, you might have a part number 001 and 01. Numerically, they're equal, but as strings, they're two different strings. There is no way for the shell script to know. Instead, you must let the shell script know if it's a numeric comparison or a string comparison.
Perl has similar issues since you don't declare variables as numeric or non-numeric:
Shell Script Perl
Boolean Operator Numeric String Numeric String
=================== ======= ====== ======= ======
Equals -eq = == eq
Not Equals -ne != != ne
Greater Than -gt > > gt
Less Than -lt < < lt
Greater or Equals -ge >= >= ge
Less Than or Equals -le <= <= le
You can try a few other things:
$ echo "*" # Echos the asterisk
$ echo * # No quotes: Prints all files in current directory
Notice again the shell expands the * before executing the echo command. This is the main difference between a shell script and a typical programming language. The shell first does expansion (fill in environment variables, glob substitution, and run sub-commands) before it actually executes the command.
The set -xv will show you what command is being executed, and how the shell expands the command before executing. Doing set +xv will shut that off. Play around with that, and you'll soon understand the shell a bit better.
the spaces are mandatory in the [ ] expression, so
if [ $x == 3 ]; then
you need a sigill on variables in the [ ] tests
For clarity, use == for equality rather than = even though both work. The former encourages the use of [[ and the latter can be confused with an assignment. Use (( … )) or -lt and -gt for numerical comparison.
if [[ "${my_var}" == "val" ]]; then
do_something
fi
if (( my_var == 3 )); then
do_something
fi
if [[ "${my_var}" -gt 3 ]]; then
do_something
fi