bash 关系运算符_如何使用Bash编程:逻辑运算符和Shell扩展

bash 关系运算符

Bash是一种强大的编程语言,一种完美设计用于命令行和Shell脚本的语言。 这个由三部分组成的系列文章(基于我的三卷Linux自学课程 )探讨了如何在命令行界面(CLI)上使用Bash作为编程语言。

第一篇文章探讨了一些使用Bash的简单命令行编程,包括使用变量和控件运算符。 第二篇文章介绍了文件,字符串,数字和其他逻辑运算符的类型,这些运算符在Bash中提供了执行流控制逻辑和不同类型的Shell扩展。 本系列的第三篇也是最后一篇文章将探讨forwhile直到循环,以实现重复操作。

逻辑运算符是在程序中进行决策并基于这些决策执行不同指令集的基础。 有时称为流量控制。

逻辑运算符

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 ] $

现在列出以Dotestdir / Documentstestdir / 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扩展。

本系列的第三篇文章将探讨使用循环执行各种类型的迭代操作。

翻译自: https://opensource.com/article/19/10/programming-bash-logical-operators-shell-expansions

bash 关系运算符

你可能感兴趣的:(字符串,shell,python,linux,java)