When writing scripts in Bash, not only experienced programmers, but also newcomers to the Bash shell, are faced with working with strings. This is most often needed when reading commands that the user enters as arguments to an executable script, and when processing text files. And one of the necessary tricks in this case is a string comparison.

This article will look at comparing Bash strings, as well as some of the nuances of using comparison operations and solving common errors.

These operations allow you to determine whether the compared strings are the same:

  • = - equal, for example if [ "$x" = "$y" ]
  • == - synonym for the "=" operator, for example if [ "$x" == "$y" ]
  • != - not the same, for example if [ "$x" != "$y" ]

#!/bin/bash
testuser=anton
if [ $USER = $testuser ]
then
echo "Welcome $testuser"
fi

Result of the script:

When checking for equality with the command test(synonymous with square brackets ) all punctuation and case differences in the compared strings are taken into account.

Some features of string matching with patterns:

# returns true if the string contained in $x starts with "y"
[[ $x == y* ]]
# returns true if the string of $x is exactly two characters "y*"
[[ $x == "y*" ]]
# returns true if $x contains the name of a file contained in the current directory that starts with "y"
[ $x == y* ]
# returns true if the string $x is equal to two characters "y*"
[ "$x" == "y*" ]

For example, checking a bash string to see if it starts with a y:

#!/bin/bash
x=yandex
[[ $x == y* ]]
echo $?

Result of code execution:

The script outputs 0 (zero) because we asked for the error code of the last instruction executed. A code of 0 means that the script ran without errors. Indeed, the variable $x contains the string yandex, which starts with the character "y". Otherwise, "1" may be written. This is a pretty handy way to debug scripts.

Comparing strings alphabetically in Bash

The task gets more complicated when trying to determine if a string is the predecessor of another string in an ascending sort order. People who write scripts in the bash shell often run into two problems regarding greater-than and less-than operations with Linux string comparisons, which have fairly simple solutions:

First, the "greater than" and "less than" characters must be escaped by adding a backslash (\) in front of them, because otherwise they will be treated by the shell as redirection characters, and strings as filenames. This is one of those cases where tracking down a bug is quite difficult.

#!/bin/bash
# incorrect use of string comparison operators
val1=baseball
val2=hockey
if [ $val1 > $val2 ]
then

else

fi

What happens if you compare bash strings:

As you can see, the "greater than" symbol alone, in its direct form, produced incorrect results, although no errors were generated. In this case, this character caused the output stream to be redirected, so no syntax errors were found and, as a result, a file named hockey:

To fix this error, you need to escape the ">" character so that the condition looks like this:

...
if [ $val1 \> $val2 ]
...

Then the result of the program will be correct:

Secondly, the strings ordered using the "greater than" and "less than" operators are arranged differently than it happens with the command sort. Here, problems are more difficult to recognize, and they may not be encountered at all if the comparison is case-insensitive. In a team sort and test The comparison is different:

#!/bin/bash
val1=Testing
val2=testing
if [ $val1 \> $val2 ]
then
echo "$val1 is greater than $val2"
else
echo "$val1 is less than $val2"
fi

The result of the code:

In a team test uppercase strings will first precede lowercase strings. But if the same data is written to a file, to which then apply the command sort, then strings with lowercase letters will come first:

The difference between their work is that test to determine the sort order, the arrangement of characters according to the ASCII table is taken as a basis. AT sort it also uses the sort order specified for the regional settings language settings.

Checking a string for an empty value

Comparison using operators -z and -n used to determine whether a variable contains content. This way you can find empty lines in bash. Example:

#!/bin/bash
val1=testing
val2=""
# checks if the string is empty
if [ -n $val1 ]
then
echo "String "$val1" is not empty"
else
echo "String "$val1" is empty"
fi
# checks if the string is empty
if [ -z $val2 ]
then
echo "String "$val2" is empty"
else
echo "String "$val2" is not empty"
fi
if [ -z $val3 ]
then
echo "String "$val3" is empty"
else
echo "String "$val3" is not empty"
fi

The result of the code:

This example creates two string variables - val1 and val2. Operation -n determines if a variable has val1 non-zero length, and -z checks val2 and val3 to zero. It is noteworthy that the latter was not defined until the moment of comparison, but the interpreter considers that its length is still equal to zero. This nuance should be taken into account in various scenario checks. And, if you are not sure what value is contained in the variable and whether it is set at all, you should check it using the operator -n or -z and only then use it for its intended purpose.

It is worth paying attention to the function -n. If an undeclared or empty variable is passed to it to test, it will return true, not false. For such cases, you should enclose the string (variable) being checked in double quotes so that it looks like this:

...
if [ -n "$val1" ]
...

conclusions

There are certain nuances to the Bash string comparison operations presented that are worth understanding in order to prevent scripting errors. But there are many such situations in practice, so remembering everything (and even more so, describing) will not work.

if-else condition applied in BASH scripts Often. The condition itself has a somewhat strange form [[ condition ]]. Pay attention to the indents. Without them, the condition will not work. I give a list of logical operators for the condition [[ ? ]]:

List of logical operators that
used for the if-then-else-fi construct

#!/bin/bash if [[ $1 > 2 ]] then # if [[ ? ]] echo $1" greater than 2" else # if not true echo $1" less than 2 or 2" fi

Some of you will find the equality operator -eq strange. Try using familiar operators >

Let's say you have a script and user verification is required. If the user is not root, then the script will stop.

#!/bin/bash if [ "$(whoami)" != "root" ]; then echo "You do not have permission to run $0." exit1; fi

Often you need to check a variable for a value. If there is nothing in the variable, then you can stop the script.

#!/bin/bash if [ -n "$num" ]; then "the variable has something and you can start another process" else echo "empty variable, stop the script" exit 0; fi

If the variable is empty, then it can be filled.

#!/bin/bash if [ -z "$num" ]; then echo "variable is empty" num=1 else echo "num="$num fi

An empty variable can be assigned a default value. This entry is shorter than in the previous example.

#!/bin/bash # Write DEFAULT if no command line arguments [ -z "$arg1" ] && arg1=DEFAULT echo $arg1

In Bash shell scripts, we can perform number comparisons. To perform a number comparison operation in Bash, you must use the "test" state within an if or loop. In this post, we will tell you how to compare numbers in bash.

Operators for bash number comparison

operatorwhat is he doingexample
-eqcompare digits in bash for equality, returns 0 if equalif [ $a -eq $b ] then
-gecomparing numbers in bash if greater than or equal. Result returns 0 if greater than or equal toif [ $a -ge $b ] then
-gtcompares numbers in bash if greater than.if [ $a -gt $b ] then
-lecompares numbers in bash if less than or equal.if [ $a -le $b ] then
-ltcompares numbers in bash if less.if [ $a -lt $b ] then
-necompares numbers in bash if not equal or not.if [ $a -ne $b ] then

Examples in detail of number comparison operators in Bash:

1. operator-eq

This operator compares numbers, will check if the value is equal or not. If it is equal, then 0 is returned.

# cat test.sh #!/bin/bash echo "enter variable value" read a echo "enter variable value" read b if [ $a -eq $b ] then echo "Return value:: $?" echo "a and b are equal" else echo "Return value::$?" echo "a and b are not equal" fi #

Execution:

# sh test.sh enter variable value 2 enter variable value 3 Return value:: 1 a and b are not equal # sh test.sh enter variable value 2 enter variable value 2 Return value:: 0 a and b are equal #

In the example above, we took the numbers 2 and 3 for the first time and the system returned the value 1, however, when we took the same values ​​for a and b, the variable returns zero.

2. operator-ge

This operator compares numbers and checks for values ​​greater than or equal to. If the value is greater than or equal to, then it's return value is 0.

# cat test1.sh #!/bin/bash #diff program for -ge echo "enter value for variable" read a echo "enter value for variable b" read b if [ $a -ge $b ] then echo "return value: :$?" echo "a is greater than or equal to b" else echo "return value::$?" echo "a is not greater than or equal to b" fi #

3. operator -gt

This number comparison operator will test the number to be greater. If the value is greater, then it returns 0.

# cat test2.sh #!/bin/bash #differ for -gt b=100 echo "Enter a value greater than 100" read a if [ $a -gt $b ] then echo "Very good" else echo "Not very good " fi

4. operator-le

This number comparison operator will test values ​​for less than or equal to. If it is less than or equal, then the return value is 0.

#diff program for -le b=5 echo "enter a value less than or equal to 5" read a if [ $a -le $b ] then echo "correct" else echo "false" fi #

5. operator-lt

This number comparison operator will test values ​​for less. If the number is less then the return value is 0.

Comparing strings in Bash does not cause any problems until the task arises to compare two strings in a case-insensitive manner. I will give several options for solving the problem that I use myself. A feature of these solutions is to use only the built-in features of the Bash shell.

First, I'll create two variables str1 and str2 containing the strings to be compared. They will be used in the following code examples.

#!/bin/bash str1 = "String To Compare" str2 = "string to compare"

The first version of the case-insensitive string comparison that I want to propose uses the control of shell options with the built-in shopt command.

shopt -s nocasematch [[ $str1 == $str2 ]] && echo "match" || echo "not match" shopt -u nocasematch

The next version of case-insensitive string comparison is based on the principle of self-casting strings to a common case. This code variant works on Bash version 4 and newer. Using it on an earlier version of Bash will result in an error.

So, to compare strings cast to lowercase, you can use the following code.

[[ " $( str1 , ) " == " $( str2 , ) " ]] && echo "match" || echo "not match"

If you want to convert the compared strings to upper case, you can use the following code.

[[ " $( str1 ^^ ) " == " $( str2 ^^ ) " ]] && echo "match" || echo "not match"

Alternatively, strings can be coerced at the time of variable declaration. This is done using the declare shell built-in command.

To declare a variable containing lowercase text, use the following code.

#!/bin/bash declare -l str = "Camel Case String"

As a result of executing this code, the str variable will contain a lowercase string, despite the fact that the string being assigned was written in camel case. You can change the case of a string already set in a variable as follows.

#!/bin/bash str = "Camel Case String" declare -l str str = $str echo $str

To convert a string to uppercase, in the above code example, you should change the call to the declare command, using the -u switch instead of the -l switch.

Now a case-insensitive string comparison using the declare command can be done as follows.

declare -l str1_l = $str1 declare -l str2_l = $str2 [[ $str1_l == $str2_l ]] && echo "match" || echo "not match"

Any of these case-insensitive string comparisons can be used in Bash scripting. Therefore, if you are using Bash version 4 and above, you can choose the one that you like best. If the Bash version is less than 4, then the first option should be used, specifying the nocasematch option using the shopt shell builtin.

This topic is the fourth topic in the bash shell language series. He will talk about such control structures of the language as conditional statements. But before proceeding to their description, it is necessary to dwell on some nuances that will make the consideration of the material below more understandable.
First, let's look at what a list of commands is. A command list is a single command, a pipeline, or a sequence of commands/pipelines separated by one of the following operators: ";", "&&", "||", terminated by a semicolon.
; - operator of sequential execution of several commands. Each subsequent command starts executing only after the completion of the previous one (whether successful or not);
&& - command execution operator only after successful execution of the previous one;
|| - statement for executing a command only after an erroneous execution of the previous one.
The success code is 0, and the error code is not zero (depending on the type of error). Not to be confused with common programming languages ​​when 1 is true and 0 is false.
Now we can proceed to the direct consideration of conditional statements.

case variant operator

The general syntax of the case statement is:

case value in
template1) list1;;
pattern2 | template3) list2;;
esac

The logical sequence of execution of the case statement:
a) the first pattern matching the value is searched;
b) if it is found, the list of commands corresponding to it is executed, terminated by ";;";
c) control is transferred to the operators following the case construction.
The pattern and the list are separated by the character ")". Several conditions can correspond to one list of commands, in which case they must be separated by the "|" symbol.
In templates, you can use the characters "*", "?", "", which were described in the second topic of the cycle. With their help, you can implement an instruction that acts as default in the switch statement of such languages ​​as C, PHP.
I will give an example of using case:
echo -n "[Universal Viewer] Specify filename: "; read File case "$File" in *.jpg|*.gif|*.png) eog $File ;; *.pdf) evince $File ;; *.txt) less $File ;; *.html) firefox $File ;; /dev/*) echo "Well, those are scary files." ;; *) echo "Ok, ok - not so versatile." echo "I'm not familiar with this file type. I don't know how to view it." ;; esac
Another example of using the case construct:
echo "Error. Whom should I send the message to?" echo "To boss: b" echo "To colleagues: c" echo "To nobody: any key" read answer case $answer in b|B) mail –s "error log" boss< error.log;; c|C) mail –s "Help! error log" –c denis nick < error.log;; *) echo "error"; exit;; esac

Conditional if statement

The general syntax of the if statement is:

if list1 then
list2

fi

The square brackets here indicate optional constructs. The logical sequence of execution of the case statement:
a) list1 is executed;
b) if it is executed without errors, list2 is executed. Otherwise, list3 is executed, and if it completes without error, list4. If list3 also returns an error code, list5 is executed;
c) control is transferred to the operators following the if construct.
I will give an example of using if:
if grep -q Bash file then echo "File contains at least one Bash word." fi
When if and then are on the same line, then the if and then constructs must end with a semicolon. For example:
$if[$? –ne0]; then echo "Error"; fi
Now, knowing that it's possible to put if and then on the same line, let's rewrite the above example:
if grep -q Bash file; then echo "File contains the word Bash." fi

test statement and conditional expressions

In the above example, instead of parsing the exit code, a condition check is used. The two forms of such a test are equivalent: the test built-in command and [condition]. For example, to check if a file exists, you would write:
test -e<файл>
or
[-e<файл> ]
If square brackets are used, they must be separated from each other by a space, because "[" is the name of the command, and "]" is the required last argument to terminate it.
If the condition is successfully checked, 0 is returned, and if it is false, the error code is 1.
The test command can check if a string is empty. A non-empty string results in exit code 0. Empty, respectively - 1. For example:
$ test $USER; echo $? 0
The construction "" is more versatile than "". This extended version of the test command. Inside this construction, no additional interpretation of file names is performed and no splitting of arguments into separate words is performed, but substitution of parameters and commands is allowed. For example:
file=/etc/passwd if [[ -e $file ]] then echo “Password file found.” fi
The construction "" is more preferable than "" because it will help to avoid some logical errors. For example, the operators "&&", "||", "<" и ">" inside "" are perfectly valid, while inside "" generate error messages.
The construction "(())" allows the calculation of arithmetic expressions inside it. If the result of the calculation is zero, then an error code is returned. A non-zero result of the calculation gives a return code of 0. That is, the exact opposite of the test and "" instructions discussed above.
The if statement allows nested checks:
if echo "The next *if* is inside the first *if*." if [[ $comparison = "integer" ]] then ((a< b)) else [[ $a < $b ]] fi then echo "$a меньше $b" fi

Conditional expressions can be combined using the usual logical operations:
! <выражение>- denial;
<выражение1>-a<выражение2>- logical AND;
<выражение1>–o<выражение2>- logical OR.

Elementary conditional expressions for files:
-e - file exists;
-f - regular file (not directory or device file);
-s - non-zero file size;
-d - the file is a directory;
-b - the file is a block device (floppy, cdrom, etc.);
-c - the file is a character device (keyboard, modem, sound card, etc.);
-p - the file is a pipe;
-h - the file is a symbolic link;
-L - the file is a symbolic link;
-S - the file is a socket;
-t - the file is associated with a terminal device;
-r - the file is readable (to the user who launched the script);
-w - the file is writable (to the user who ran the script);
-x - the file is available for execution (to the user who launched the script);
-g - (sgid) flag for file or directory is set;
-u - (suid) the flag for the file is set;
-k - sticky bit flag is set;
-O - ​​you are the owner of the file;
-G - you belong to the same group as the file;
-N - the file has been modified since the last read;
file1 -nt file2 – file1 is newer than file2;
file1 -ot file2 – file1 is older than file2;
file1 -ef file2 - file1 and file2 are hard links to the same file.

Elementary conditional expressions for comparing strings:
-z string – string length is 0;
-n string – string length is not equal to 0;
string1 == string2 – strings match (similar to “=”);
line1 !== line2 – lines do not match (similar to “!=”);
line1< строка2 – строка1 предшествует строке2 в лексикографическом порядке;
line1 > line2 - line1 follows line2 in lexicographic order.
An arithmetic conditional expression has the format:
argument1 operation argument2 where the arguments are integers and the following operations are allowed:
-eq - equal;
-ne - not equal;
-lt - less;
-le - less than or equal;
-gt - more;
-ge - greater than or equal;
< - меньше (внутри двойных круглых скобок);
<= - меньше или равно (внутри двойных круглых скобок);
> - greater than (inside double parentheses);
>= - greater than or equal to (inside double parentheses).

Let's rewrite the previous example using an if statement:
echo "Error. Whom should I send the message to?" echo "To boss: b" echo "To colleagues: c" echo "To nobody: any key" read answer if [ "$answer" == "b" –o "$answer" == "B" ]; then mail –s "error log" boss< error.log; elif [ "$answer" == "c" –o "$answer" == "C" ]; then mail –s "Help! error log" –c denis nick < error.log; else echo "error"; exit; fi

In the next topic, I will continue to consider the control structures of the bash shell. Namely, loop operators will be considered. And now I'm waiting for comments and criticism :).

UPD: Thanks to the user