bash 关系运算符
Bash是一种强大的编程语言,一种完美设计用于命令行和Shell脚本的语言。 这个由三部分组成的系列文章(基于我的三卷Linux自学课程 )探讨了如何在命令行界面(CLI)上使用Bash作为编程语言。
第一篇文章探讨了一些使用Bash的简单命令行编程,包括使用变量和控件运算符。 第二篇文章介绍了文件,字符串,数字和其他逻辑运算符的类型,这些运算符在Bash中提供了执行流控制逻辑和不同类型的Shell扩展。 本系列的第三篇也是最后一篇文章将探讨for , while和直到循环,以实现重复操作。
逻辑运算符是在程序中进行决策并基于这些决策执行不同指令集的基础。 有时称为流量控制。
Bash有大量可用于条件表达式的逻辑运算符。 if控制结构的最基本形式是测试条件,然后在条件为真时执行程序语句列表。 共有三种类型的运算符:文件运算符,数字运算符和非数字运算符。 如果满足条件,则每个运算符返回true(0),如果不满足条件,则返回false(1)。
这些比较运算符的功能语法是一个或两个自变量,运算符放在方括号内,如果条件为true,则后面是要执行的程序语句的列表,如果条件为false,则是可选的程序语句的列表。 :
if
[ arg1 operator arg2
] ;
then list
or
if
[ arg1 operator arg2
] ;
then list ;
else list ;
fi
比较中的空格是必需的,如图所示。 单个方括号[和]是与测试命令等效的传统Bash符号:
if test arg1 operator arg2 ; then list
还有一种较新的语法,它提供了一些优点,有些系统管理员更喜欢这种语法。 这种格式与不同版本的Bash和其他shell(例如ksh(Korn shell))的兼容性稍差。 看起来像:
if [ [ arg1 operator arg2 ] ] ; then list
文件运算符是Bash中功能强大的一组逻辑运算符。 图1列出了Bash可以对文件执行的20多种不同的运算符。 我在脚本中经常使用它们。
操作员 | 描述 |
---|---|
-a filename | 如果文件存在,则为true;否则为false。 它可以为空或具有某些内容,但是,只要存在,就为真 |
-b filename | 如果文件存在且为块特殊文件(例如,像/ dev / sda或/ dev / sda1这样的硬盘驱动器),则为true |
-c filename | 如果文件存在并且是字符特殊文件(例如,TTY设备,例如/ dev / TTY1) ,则为True |
-d filename | 如果文件存在并且是目录,则为True |
-e filename | 如果文件存在,则为true;否则为false。 这与上面的-a相同 |
-f filename | 如果文件存在并且是常规文件(与目录,设备专用文件或链接等相对),则为True |
-g filename | 如果文件存在并且为set-group-id , 则为True |
-h filename | 如果文件存在并且是符号链接,则为True |
-k filename | 如果文件存在并且其“ sticky”位已设置,则为True |
-p filename | 如果文件存在且为命名管道(FIFO),则为True |
-r filename | 如果文件存在且可读,即设置了读取位,则为True |
-s filename | 如果文件存在且大小大于零,则为true;否则为false。 存在但大小为零的文件将返回false |
-t fd | 如果文件描述符fd已打开并指向终端,则为true |
-u filename | 如果文件存在并且设置了它的set-user-id位,则为True |
-w filename | 如果文件存在且可写,则为True |
-x filename | 如果文件存在并且可执行,则为True |
-G filename | 如果文件存在且由有效组ID拥有,则为True |
-L filename | 如果文件存在并且是符号链接,则为True |
-N filename | 如果文件存在并且自上次读取以来已被修改,则为True |
-O filename | 如果文件存在并且由有效用户ID拥有,则为True |
-S filename | 如果文件存在并且是套接字,则为True |
file1 -ef file2 | 如果file1和file2引用相同的设备和iNode编号,则为true |
file1 -nt file2 | 如果file1比file2更新(根据修改日期),则为true,或者如果file1存在且file2不存在 |
file1 -ot file2 | 如果file1早于file2,或者file2存在且file1不存在,则为true |
图1:Bash文件运算符
例如,首先测试文件是否存在:
[ student
@ studentvm1 testdir
] $
File =
"TestFile1" ;
if
[
-e
$File
] ;
then
echo
"The file $File exists." ;
else
echo
"The file $File does not exist." ;
fi
The
file TestFile1 does not exist.
[ student
@ studentvm1 testdir
] $
接下来,创建一个名为TestFile1的测试文件。 目前,它不需要包含任何数据:
[ student @ studentvm1 testdir ] $ touch TestFile1
在这个简短的CLI程序中,可以很容易地在多个位置更改$ File变量的值,而不是更改文件名的文本字符串:
[ student
@ studentvm1 testdir
] $
File =
"TestFile1" ;
if
[
-e
$File
] ;
then
echo
"The file $File exists." ;
else
echo
"The file $File does not exist." ;
fi
The
file TestFile1 exists.
[ student
@ studentvm1 testdir
] $
现在,运行测试以确定文件是否存在并且长度为非零,这意味着它包含数据。 您要测试三个条件:1.文件不存在; 2.文件存在且为空; 3.该文件存在并包含数据。 因此,您需要一组更复杂的测试-在if-elif-else构造中使用elif节来测试所有条件:
[ student
@ studentvm1 testdir
] $
File =
"TestFile1" ;
if
[
-s
$File
] ;
then
echo
" $File exists and contains data." ;
fi
[ student
@ studentvm1 testdir
] $
在这种情况下,该文件存在但不包含任何数据。 添加一些数据,然后重试:
[ student
@ studentvm1 testdir
] $
File =
"TestFile1" ;
echo
"This is file $File "
>
$File ;
if
[
-s
$File
] ;
then
echo
" $File exists and contains data." ;
fi
TestFile1 exists and contains data.
[ student
@ studentvm1 testdir
] $
这是可行的,但仅对三种可能情况中的一种特定情况才真正准确。 添加else节,这样您可以更准确一些,并删除文件,以便完全测试以下新代码:
[ student
@ studentvm1 testdir
] $
File =
"TestFile1" ;
rm
$File ;
if
[
-s
$File
] ;
then
echo
" $File exists and contains data." ;
else
echo
" $File does not exist or is empty." ;
fi
TestFile1 does not exist or is empty.
现在创建一个空文件进行测试:
[ student
@ studentvm1 testdir
] $
File =
"TestFile1" ;
touch
$File ;
if
[
-s
$File
] ;
then
echo
" $File exists and contains data." ;
else
echo
" $File does not exist or is empty." ;
fi
TestFile1 does not exist or is empty.
将一些内容添加到文件中,然后再次测试:
[ student
@ studentvm1 testdir
] $
File =
"TestFile1" ;
echo
"This is file $File "
>
$File ;
if
[
-s
$File
] ;
then
echo
" $File exists and contains data." ;
else
echo
" $File does not exist or is empty." ;
fi
TestFile1 exists and contains data.
现在,添加elif节以区分不存在的文件和空的文件:
[ student
@ studentvm1 testdir
] $
File =
"TestFile1" ;
touch
$File ;
if
[
-s
$File
] ;
then
echo
" $File exists and contains data." ;
elif
[
-e
$File
] ;
then
echo
" $File exists and is empty." ;
else
echo
" $File does not exist." ;
fi
TestFile1 exists and is empty.
[ student
@ studentvm1 testdir
] $
File =
"TestFile1" ;
echo
"This is $File "
>
$File ;
if
[
-s
$File
] ;
then
echo
" $File exists and contains data." ;
elif
[
-e
$File
] ;
then
echo
" $File exists and is empty." ;
else
echo
" $File does not exist." ;
fi
TestFile1 exists and contains data.
[ student
@ studentvm1 testdir
] $
现在,您有了一个Bash CLI程序,可以测试这三种不同的条件……但是可能性是无限的。
如果将程序语句安排得更像可以保存在文件中的脚本,则更容易看到更复杂的复合命令的逻辑结构。 图2显示了外观。 if-elif-else结构的每个节中的程序语句的缩进有助于阐明逻辑。
File="TestFile1"
echo "This is $File" > $File
if [ -s $File ]
then
echo "$File exists and contains data."
elif [ -e $File ]
then
echo "$File exists and is empty."
else
echo "$File does not exist."
fi
图2:命令行程序被重写,就像在脚本中一样
对于大多数CLI程序来说,这种复杂的逻辑太冗长了。 尽管CLI程序中可以使用任何Linux或Bash内置命令,但是随着CLI程序变得越来越长和越来越复杂,创建存储在文件中并可以随时执行的脚本更加有意义。在将来。
字符串比较运算符可以比较字母数字字符串。 这些运算符只有很少一部分,在图3中列出。
操作员 | 描述 |
---|---|
-z string | 如果字符串的长度为零,则为真 |
-n string | 如果字符串的长度非零,则为真 |
string1 == string2 or string1 = string2 |
如果字符串相等,则为true;否则为true。 测试命令应使用单个=来实现POSIX一致性。 当与[[命令]一起使用时,它执行如上所述的模式匹配(复合命令)。 |
string1 != string2 | 如果字符串不相等则为真 |
string1 < string2 | 如果string1在字典上排在string2之前,则为true (指的是针对所有字母数字和特殊字符的特定于语言环境的排序序列) |
string1 > string2 | 如果string1在字典上排在string2之后,则为true |
图3:Bash字符串逻辑运算符
首先,查看字符串长度。 比较中必须存在$ MyVar周围的引号,才能进行比较。 (您仍应在〜/ testdir中工作 。)
[ student
@ studentvm1 testdir
] $
MyVar =
"" ;
if
[
-z
""
] ;
then
echo
"MyVar is zero length." ;
else
echo
"MyVar contains data" ;
fi
MyVar is zero length.
[ student
@ studentvm1 testdir
] $
MyVar =
"Random text" ;
if
[
-z
""
] ;
then
echo
"MyVar is zero length." ;
else
echo
"MyVar contains data" ;
fi
MyVar is zero length.
您也可以这样操作:
[ student
@ studentvm1 testdir
] $
MyVar =
"Random text" ;
if
[
-n
" $MyVar "
] ;
then
echo
"MyVar contains data." ;
else
echo
"MyVar is zero length" ;
fi
MyVar contains data.
[ student
@ studentvm1 testdir
] $
MyVar =
"" ;
if
[
-n
" $MyVar "
] ;
then
echo
"MyVar contains data." ;
else
echo
"MyVar is zero length" ;
fi
MyVar is zero length
有时您可能需要知道字符串的确切长度。 这不是比较,而是相关的。 不幸的是,没有简单的方法来确定字符串的长度。 有两种方法可以做到这一点,但我认为使用expr (求值表达式)命令最简单。 阅读expr的手册页,以了解有关其功能的更多信息。 请注意,您要测试的字符串或变量必须带引号。
[ student
@ studentvm1 testdir
] $
MyVar =
"" ;
expr length
" $MyVar "
0
[ student
@ studentvm1 testdir
] $
MyVar =
"How long is this?" ;
expr length
" $MyVar "
17
[ student
@ studentvm1 testdir
] $
expr length
"We can also find the length of a literal string as well as a variable."
70
关于比较运算符,我在脚本中使用了大量测试来确定两个字符串是否相等(即相同)。 我使用此比较运算符的非POSIX版本:
[ student
@ studentvm1 testdir
] $
Var1 =
"Hello World" ;
Var2 =
"Hello World" ;
if
[
" $Var1 " ==
" $Var2 "
] ;
then
echo
"Var1 matches Var2" ;
else
echo
"Var1 and Var2 do not match." ;
fi
Var1 matches Var2
[ student
@ studentvm1 testdir
] $
Var1 =
"Hello World" ;
Var2 =
"Hello world" ;
if
[
" $Var1 " ==
" $Var2 "
] ;
then
echo
"Var1 matches Var2" ;
else
echo
"Var1 and Var2 do not match." ;
fi
Var1 and Var2
do not match.
您可以自己尝试一些操作,以尝试使用这些运算符。
数值运算符在两个数值参数之间进行比较。 像其他运算符类一样,大多数易于理解。
操作员 | 描述 |
---|---|
arg1 -eq arg2 | 如果arg1等于arg2为真 |
arg1 -ne arg2 | 如果arg1不等于arg2,则为true |
arg1 -lt arg2 | 如果arg1小于arg2,则为true |
arg1 -le arg2 | 如果arg1小于或等于arg2,则为true |
arg1 -gt arg2 | 如果arg1大于arg2,则为true |
arg1 -ge arg2 | 如果arg1大于或等于arg2,则为true |
图4:Bash数值比较逻辑运算符
这是一些简单的例子。 第一个实例将变量$ X设置为1,然后测试$ X是否等于1。在第二个实例中, X设置为0,因此比较不正确。
[ student
@ studentvm1 testdir
] $
X =
1 ;
if
[
$X
-eq
1
] ;
then
echo
"X equals 1" ;
else
echo
"X does not equal 1" ;
fi
X equals
1
[ student
@ studentvm1 testdir
] $
X =
0 ;
if
[
$X
-eq
1
] ;
then
echo
"X equals 1" ;
else
echo
"X does not equal 1" ;
fi
X does not equal
1
[ student
@ studentvm1 testdir
] $
自己尝试其他一些实验。
这些其他运算符显示了是否设置了shell选项或shell变量是否具有值,但它不会发现变量的值,而只是发现变量是否具有值。
操作员 | 描述 |
---|---|
-o optname | 如果启用了shell选项optname,则为true(请参阅Bash手册页中内置的Bash集的-o选项说明下的选项列表) |
-v varname | 如果设置了shell变量varname(已分配值),则为True |
-R varname | 如果已设置外壳程序变量varname并且是名称引用,则为true |
图5:其他Bash逻辑运算符
您可以自己尝试一下这些运算符。
Bash支持许多类型的扩展和替换,这些扩展和替换可能非常有用。 根据Bash的手册页,Bash具有七种扩展形式。 本文研究其中五个:波浪号扩展,算术扩展,路径名扩展,花括号扩展和命令替换。
括号扩展是一种生成任意字符串的方法。 (下面使用此工具来创建大量文件,以供使用特殊模式字符进行实验。)括号扩展可用于生成任意字符串列表,并将其插入到封闭的静态字符串内的特定位置或插入字符串的任意一端。静态字符串。 这可能很难可视化,所以最好做到这一点。
首先,这是大括号扩展的作用:
[ student
@ studentvm1 testdir
] $
echo
{ string1,string2,string3
}
string1 string2 string3
好吧,那不是很有帮助,是吗? 但是,看看使用它时会发生什么变化:
[ student
@ studentvm1 testdir
] $
echo
"Hello "
{ David,Jen,Rikki,Jason
} .
Hello David. Hello Jen. Hello Rikki. Hello Jason.
看起来很有用-可以节省很多打字时间。 现在尝试这个:
[ student
@ studentvm1 testdir
] $
echo b
{
ed ,olt,
ar
} s
beds bolts bars
我可以继续,但是你明白了。
可以说,最常见的扩展是波浪号( 〜 )扩展。 当您在诸如cd〜/ Documents之类的命令中使用此命令时,Bash shell会将其展开为用户完整主目录的快捷方式。
使用以下Bash程序观察波浪号扩展的效果:
[ student
@ studentvm1 testdir
] $
echo ~
/ home
/ student
[ student
@ studentvm1 testdir
] $
echo ~
/ Documents
/ home
/ student
/ Documents
[ student
@ studentvm1 testdir
] $
Var1 =~
/ Documents ;
echo
$Var1 ;
cd
$Var1
/ home
/ student
/ Documents
[ student
@ studentvm1 Documents
] $
路径名扩展是一个花哨的术语,它使用字符?扩展文件全局模式。 和* ,匹配模式的目录全名。 文件通配指的是特殊模式字符,当执行各种操作时,它们可以在匹配文件名,目录和其他字符串时提供极大的灵活性。 这些特殊的模式字符允许匹配字符串中的单个,多个或特定字符。
此扩展适用于匹配的目录名称。 要查看其工作原理,请确保testdir是当前的工作目录(PWD),并以简单列表开头(我的主目录的内容与您的主目录的内容不同):
[ student
@ studentvm1 testdir
] $
ls
chapter6 cpuHog.dos dmesg1.txt Documents Music softlink1 testdir6 Videos
chapter7 cpuHog.Linux dmesg2.txt Downloads Pictures Templates testdir
testdir cpuHog.mac dmesg3.txt file005 Public testdir tmp
cpuHog Desktop dmesg.txt link3 random.txt testdir1 umask.test
[ student
@ studentvm1 testdir
] $
现在列出以Do , testdir / Documents和testdir / Downloads开头的目录:
Documents:
Directory01 file07 file15 test02 test10 test20 testfile13 TextFiles
Directory02 file08 file16 test03 test11 testfile01 testfile14
file01 file09 file17 test04 test12 testfile04 testfile15
file02 file10 file18 test05 test13 testfile05 testfile16
file03 file11 file19 test06 test14 testfile09 testfile17
file04 file12 file20 test07 test15 testfile10 testfile18
file05 file13 Student1.txt test08 test16 testfile11 testfile19
file06 file14 test01 test09 test18 testfile12 testfile20
Downloads:
[ student
@ studentvm1 testdir
] $
好吧,那没有做您想要的。 它列出了以Do开头的目录的内容。 要仅列出目录而不列出目录,请使用-d选项。
[ student
@ studentvm1 testdir
] $
ls
-d Do
*
Documents Downloads
[ student
@ studentvm1 testdir
] $
在这两种情况下,Bash shell都将Do *模式扩展为与该模式匹配的两个目录的名称。 但是,如果还有匹配模式的文件怎么办?
[ student
@ studentvm1 testdir
] $
touch Downtown ;
ls
-d Do
*
Documents Downloads Downtown
[ student
@ studentvm1 testdir
] $
这也显示了文件。 因此,与该模式匹配的所有文件也将扩展为其全名。
命令替换是一种扩展形式,它允许一个命令的STDOUT数据流用作另一命令的参数。 例如,作为循环中要处理的项目的列表。 Bash手册页说:“命令替换允许命令输出替换命令名称。” 我觉得如果有点钝,那是正确的。
这种替换有两种形式: `command`和$(command) 。 在使用反斜线( ` )的较旧形式中,在命令中使用反斜杠( \ )保留其字面含义。 但是,当以较新的括号形式使用它时,反斜杠将其含义视为特殊字符。 还要注意,括号形式仅使用单个括号来打开和关闭命令语句。
我经常在命令行程序和脚本中使用此功能,其中一个命令的结果可用作另一个命令的参数。
从一个非常简单的示例开始,该示例使用此扩展的两种形式(再次,确保testdir为PWD):
[ student
@ studentvm1 testdir
] $
echo
"Todays date is `date` "
Todays
date is Sun Apr
7
14 :
42 :
46 EDT
2019
[ student
@ studentvm1 testdir
] $
echo
"Todays date is $(date) "
Todays
date is Sun Apr
7
14 :
42 :
59 EDT
2019
[ student
@ studentvm1 testdir
] $
seq实用程序的-w选项将前导零添加到所生成的数字中,以使它们的宽度相同,即,无论值如何,其位数均相同。 这使得按数字顺序对它们进行排序变得更加容易。
seq实用程序用于生成数字序列:
[ student
@ studentvm1 testdir
] $
seq
5
1
2
3
4
5
[ student
@ studentvm1 testdir
] $
echo
`
seq
5
`
1
2
3
4
5
[ student
@ studentvm1 testdir
] $
现在您可以做一些更有用的事情,例如创建大量用于测试的空文件:
[ student @ studentvm1 testdir ] $ for I in $ ( seq -w 5000 ) ; do touch file- $I ; done
在这种用法中,语句seq -w 5000生成一个从1到5,000的数字列表。 通过使用命令替换为for语句的一部分,编号的列表由for语句生成的文件名的数字部分。
Bash可以执行整数数学运算,但是相当麻烦(您很快就会看到)。 算术扩展的语法是$((arithmetic-expression)) ,使用双括号来打开和关闭表达式。
算术扩展的工作原理类似于Shell程序或脚本中的命令替换。 从表达式计算出的值将替换该表达式,以供外壳程序进一步评估。
再一次,从简单的事情开始:
[ student
@ studentvm1 testdir
] $
echo $
(
(
1 +
1
)
)
2
[ student
@ studentvm1 testdir
] $
Var1 =
5 ;
Var2 =
7 ;
Var3 =$
(
( Var1
* Var2
)
) ;
echo
"Var 3 = $Var3 "
Var
3 =
35
以下除法结果为零,因为结果将是小于一的十进制值:
[ student
@ studentvm1 testdir
] $
Var1 =
5 ;
Var2 =
7 ;
Var3 =$
(
( Var1
/ Var2
)
) ;
echo
"Var 3 = $Var3 "
Var
3 =
0
这是我经常在脚本或CLI程序中进行的简单计算,它告诉我Linux主机中有多少虚拟内存。 free命令不提供该数据:
[ student
@ studentvm1 testdir
] $
RAM =
`
free
|
grep ^Mem
|
awk
'{print $2}'
` ;
Swap =
`
free
|
grep ^Swap
|
awk
'{print $2}'
` ;
echo
"RAM = $RAM and Swap = $Swap " ;
echo
"Total Virtual memory is $((RAM+Swap) )" ;
RAM =
4037080 and Swap =
6291452
Total Virtual memory is
10328532
我使用`字符来分隔用于命令替换的代码段。
我主要使用Bash算术扩展来检查脚本中的系统资源量,然后根据结果选择程序执行路径。
本文是本系列的第二篇Bash编程语言文章,探讨了Bash文件,字符串,数字和其他逻辑运算符,这些运算符提供了执行流控制逻辑和不同类型的Shell扩展。
本系列的第三篇文章将探讨使用循环执行各种类型的迭代操作。
翻译自: How to program with Bash: Logical operators and shell expansions | Opensource.com
bash 关系运算符