Shell脚本编程之条件测试语句


在Linux中,条件测试(也称为条件判断或条件表达式)是用于在脚本或命令行中基于某些条件的真或假来执行不同操作的一种机制。常用于Shell脚本中,条件测试可以帮助编写控制流语句,例如if、while、until和for循环。以下是一些详细信息和常见用法:

基础概念

条件测试的结果是一个布尔值:真(true)或假(false)。

若真,则状态码变量$?返回0
若假,则状态码变量$?返回1(非零值)

条件测试命令:

test EXPRESSION
[ EXPRESSION ]      和test等价,建议使用[]
[[ EXPRESSION ]]    相当于增强版的[],支持[]的用法,且支持扩展正则表达式和通配符

注意:EXPRESSION前后必须有空白字符
[root@leiRocky ~]# type [
[ is a shell builtin
[root@leiRocky ~]# help [
[: [ arg... ]
    Evaluate conditional expression.
    
    This is a synonym for the "test" builtin, 
    but the last argument must be a literal `]', to match the opening `['.

帮助:help test

[root@leiRocky ~]# help test
test: test [expr]
    Evaluate conditional expression.
    
    Exits with a status of 0 (true) or 1 (false) depending on the evaluation of EXPR.    
    Expressions may be unary or binary.  
    Unary expressions are often used to examine the status of a file.  
    There are string operators and numeric comparison operators as well.
    
    The behavior of test depends on the number of arguments.  
    Read the bash manual page for the complete specification.
    
    File operators:
    
      -a FILE        True if file exists.
      -b FILE        True if file is block special.
      -c FILE        True if file is character special.
      -d FILE        True if file is a directory.
      -e FILE        True if file exists.
      -f FILE        True if file exists and is a regular file.
      -g FILE        True if file is set-group-id.
      -h FILE        True if file is a symbolic link.
      -L FILE        True if file is a symbolic link.
      -k FILE        True if file has its `sticky' bit set.
      -p FILE        True if file is a named pipe.
      -r FILE        True if file is readable by you.
      -s FILE        True if file exists and is not empty.
      -S FILE        True if file is a socket.
      -t FD          True if FD is opened on a terminal.
      -u FILE        True if the file is set-user-id.
      -w FILE        True if the file is writable by you.
      -x FILE        True if the file is executable by you.
      -O FILE        True if the file is effectively owned by you.
      -G FILE        True if the file is effectively owned by your group.
      -N FILE        True if the file has been modified since it was last read.
    
      FILE1 -nt FILE2  True if file1 is newer than file2 (according to modification date).
    
      FILE1 -ot FILE2  True if file1 is older than file2.
    
      FILE1 -ef FILE2  True if file1 is a hard link to file2.
    
    String operators:
    
      -z STRING      True if string is empty.
    
      -n STRING
         STRING      True if string is not empty.
    
      STRING1 = STRING2
                     True if the strings are equal.
      STRING1 != STRING2
                     True if the strings are not equal.
      STRING1 < STRING2
                     True if STRING1 sorts before STRING2 lexicographically.
      STRING1 > STRING2
                     True if STRING1 sorts after STRING2 lexicographically.
    
    Other operators:
    
      -o OPTION      True if the shell option OPTION is enabled.
      -v VAR         True if the shell variable VAR is set.
      -R VAR         True if the shell variable VAR is set and is a name
                     reference.
      ! EXPR         True if expr is false.
      EXPR1 -a EXPR2 True if both expr1 AND expr2 are true.
      EXPR1 -o EXPR2 True if either expr1 OR expr2 is true.
    
      arg1 OP arg2   Arithmetic tests.  
                     OP is one of -eq, -ne,-lt, -le, -gt, or -ge.
    
    Arithmetic binary operators return true if ARG1 is equal, not-equal,
    less-than, less-than-or-equal, greater-than, or greater-than-or-equal
    than ARG2.
    
    Exit Status:
    Returns success if EXPR evaluates to true; 
    fails if EXPR evaluates to false or an invalid argument is given.

条件测试命令

变量测试

判断shell变量是否定义,使用set是否能看到该变量的定义。

-v VAR         True if the shell variable VAR is set.
-R VAR         True if the shell variable VAR is set and is a name
[ -v NAME ]

在这里插入图片描述
在这里插入图片描述

数值测试

-eq  是否等于        (equal)
-ne  是否不等于      (not-equal)
-gt  是否大于        (greater-than)
-ge  是否大于等于     (greater-than-or-equal)
-lt  是否小于        (less-than)
-le  是否小于等于     (less-than-or-equal)
  arg1 OP arg2   Arithmetic tests.  OP is one of -eq, -ne,
                     -lt, -le, -gt, or -ge.
    
    Arithmetic binary operators return true if ARG1 is equal, not-equal,
    less-than, less-than-or-equal, greater-than, or greater-than-or-equal
    than ARG2.

在这里插入图片描述

字符串测试

-z "STRING" 字符串是否为空,没定义或空为真,不空为假, 建议加上双引号  
-n "STRING" 字符串是否不空,不空为真,空为假
   "STRING"同上
   
STRING1 = STRING2    是否等于,注意 = 前后有空格
STRING1 != STRING2   是否不等于
>   ascii码是否大于ascii码
<   是否小于
    String operators:
    
      -z STRING      True if string is empty.
    
      -n STRING
         STRING      True if string is not empty.
    
      STRING1 = STRING2
                     True if the strings are equal.
      STRING1 != STRING2
                     True if the strings are not equal.
      STRING1 < STRING2
                     True if STRING1 sorts before STRING2 lexicographically.
      STRING1 > STRING2
                     True if STRING1 sorts after STRING2 lexicographically.

在这里插入图片描述
在比较字符串时,建议变量存放在双引号""中
在这里插入图片描述

文件测试

存在性测试

-a FILE: 同-e,文件是否存在                True if file exists.
-e FILE: 文件存在性测试,存在为真,否则为假   True if file exists.
-b FILE: 是否存在且为块设备文件             True if file is block special.
-c FILE: 是否存在且为字符设备文件           True if file is character special.
-d FILE: 是否存在且为目录文件               True if file is a directory.
-f FILE: 是否存在且为普通文件               True if file exists and is a regular file.
-h FILE 或 -L FTLE: 存在且为符号链接文件       True if file is a symbolic link.
-p FILE: 是否存在且为命名管道文件           True if file is a named pipe.
-S FILE: 是否存在且为套接字文件             True if file is a socket.

范例:-e和-a 表示判断文件的存在性,建议使用-e

文件是否不存在
[root@leiRocky ~]# [ -a /etc/nologin ]
[root@leiRocky ~]# echo $?
1
[root@leiRocky ~]# ! [ -a /etc/nologin ]
[root@leiRocky ~]# echo $?
0

文件是否存在
[root@leiRocky ~]# [ -a /etc/issue ]
[root@leiRocky ~]# echo $?
0

写在中括号里面的-a取反结果没有变化,-e则有变化
[root@leiRocky ~]# [ ! -a /etc/issue ]
[root@leiRocky ~]# echo $?
0
[root@leiRocky ~]# ! [ -a /etc/issue ]
[root@leiRocky ~]# echo $?
1

因此-e为推荐写法
[root@leiRocky ~]# [ ! -e /etc/issue ]
[root@leiRocky ~]# echo $?
1
[root@leiRocky ~]# ! [ -e /etc/issue ]
[root@leiRocky ~]# echo $?
1

是否是符号链接文件
[root@leiRocky ~]# [ -L /bin ]
[root@leiRocky ~]# echo $?
0
[root@leiRocky ~]# [ -L /bin/ ]
[root@leiRocky ~]# echo $?
1
[root@leiRocky ~]#

文件权限测试

注意:最终结果由用户对文件的实际权限决定,而非文件属性决定

-r FILE: 是否存在且可读
-w FILE: 是否存在且可写
-X FILE: 是否存在且可执行
-u FILE: 是否存在且拥有suid权限
-g FILE: 是否存在且拥有sgid权限
-k FILE: 是否存在且拥有sticky权限

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

文件属性测试

-s FILE              是否存在且非空                     True if file exists and is not empty
-t fd                fd 文件描述符是否在某终端已经打开     True if FD is opened on a terminal.
-N FILE              文件自从上一次被读取之后是否被修改过   True if the file has been modified since it was last read.
-O FILE              当前有效用户是否为文件属主           True if the file is effectively owned by you.
-G FILE              当前有效用户是否为文件属组           True if the file is effectively owned by your group.
FILE1 -ef FILE2      FILE1是否是FILE2的硬链接           True if file1 is a hard link to file2.
FILE1 -nt FILE2      FILE1是否新于FILE2 (mtime)        True if file1 is newer than file2 (according to modification date).          
FILE1 -ot FILE2      FILE1是否旧于FILE2                True if file1 is older than file2.

关于()和 {}

(CMD1;CMD2;…)和{CMD1;CMD2;…;}都可以将多个命令组合在一起,批量执行。

[root@leiRocky ~]# man bash

在这里插入图片描述

( list ) 会开启子shell,并且list中变量赋值及内部命令执行后,将不再影响后续的环境。帮助参看:man bash 搜索(list)
{ list; }不会启子shell,在当前shell中运行,会影响当前shell环境。帮助参看:man bash 搜索 { list; }

()

思考下面语句的输出结果

name=mage;(echo $name; name=wang; echo $name ); echo $name
(list) list is executed in a subshell environment (see COMMAND EXECUTION ENVIRONMENT below).  Variable assignments and builtin  commands  that
       affect the shell's environment do not remain in effect after the command completes.  The return status is the exit status of list.

{ list; }
        list  is  simply  executed  in the current shell environment.  list must be terminated with a newline or semicolon.  This is known as a
        group command.  The return status is the exit status of list.  Note that unlike the metacharacters ( and ), { and } are reserved  words
        and  must occur where a reserved word is permitted to be recognized.  Since they do not cause a word break, they must be separated from
        list by whitespace or another shell metacharacter.

小括号()会开启子进程,子进程定义的变量是不会影响父进程的;第一个mage的输出是子进程继承了副进程的变量,这是一个个例。
在这里插入图片描述

() 会开启子进程
[root@leiRocky ~]# echo $BASHPID
2200
[root@leiRocky ~]# ( echo $BASHPID; sleep 100 )
2288

[root@leiRocky ~]# pstree -p
 ├─sshd(2195)───sshd(2199)───bash(2200)───bash(2288)───sleep(2289)

在这里插入图片描述

{ }

思考下面语句又是怎样的输出结果呢?

name=mage;{ echo $name; name=wang; echo $name; }; echo $name

在这里插入图片描述

{ }不会开启子shell
[root@leiRocky ~]# echo $BASHPID
2200
[root@leiRocky ~]# { echo $BASHPID; sleep 100; }
2200

[root@leiRocky ~]# pstree -p
├─sshd(2195)───sshd(2199)───bash(2200)───sleep(2356)

组合测试条件

第一种方式

说明: -a和-o需要使用测试命令进行,[[ ]]不支持。

[EXPRESSION1 -a EXPRESSION2 ]    并且,EXPRESSION1和EXPRESSTON2都是真,结果才为真
[EXPRESSION1 -o EXPRESSION2 ]    或者,EXPRESSION1和EXPRESSION2只要有一个真,结果就为真
[! EXPRESSION ]                  取反

范例:

[root@centos8 ~]#FILE="/data/scrips/test.sh"
[root@centos8 ~]#ll /data/scrips/test.sh
-rw-r--r-- 1 root root 382 Dec 23 09:32 /data/scripts/test.sh
[root@centos8 ~]#[ -f $FILE -a -x $FITLE ]
[root@centos8 ~]#echo $?
1

[root@centos8 ~]#chmod +x /data/scripts/test.sh
[root@centos8 ~]#ll /data/scripts/test.sh
-rwxr-xr-x 1 root root 382 Dec23,09:32 /data/script/test.sh

并且
[root@centos8~]#[ -f $FILE -a x $FILE ]
[root@centos8~]#echo $?
0
[root@centos8 ~]#chmod -x /data/scripts/test.sh
[root@centos8 ~]#ll /data/scripts/test.sh
-rw-r--r-- 1 root root 382 Dec 23 09:32 /data/scripts/test.sh

或者
[root@centos8 ~]#[ f $FILE -o -x $FILE ]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#[ -x $FILE ]
[root@centos8 ~]#echo $?
1

取反
[root@centos8 ~]#[ ! -x $FILE ]
[root@centos8 ~]#echo $?
[root@centos8 ~]#! [ -x $FILE ]
0

第二种方式

COMMAND1 && COMMAND2    并且,短路与,代表条件性的AND THEN 如果COMMAND1成功,将执行COMMAND2,否则,将不执行COMMAND2
COMMAND1 || COMMAND2    或者,短路或,代表条件性的OR ELSE 如果COMMAND1成功,将不执行COMMAND2,否则,将执行COMMAND2
! COMMAND               非,取反

判断用户是否存在

1.判断用户是否存在,存在就提示已经存在,否则就创建该用户。
在这里插入图片描述

[root@leiRocky ~]# id lei &> /dev/null && echo "lei is exist"
lei is exist
[root@leiRocky ~]# NAME=gouzi; id $NAME &> /dev/null || echo "$NAME is not exist"
gouzi is not exist
错误的逻辑提示

在这里插入图片描述

上图最开始的错误就是一个逻辑错误,

1.短路与和短路或的组合"cmd1 && cmd2 || cmd3",其实是一个大的短路或的组合"(cmd1 && cmd2) || cmd3"
2.当"(cmd1 && cmd2)"不成功时,cmd3才会执行,只要cmd1不成功,那么"(cmd1 && cmd2)"就是不成功的;
3.因为cmd1失败了,那"(cmd1 && cmd2)"就失败了,因此cmd2就不会执行,所以执行了cmd3;
4,相反cmd1成功了,就会执行cmd2,短路或的cmd3就不会被执行了。
正确的逻辑提示

在这里插入图片描述

正确的 && 和 || 顺序

在这里插入图片描述
结论:如果&&和||混合使用,&&要在前,ll放在后

用户不存在则创建

在这里插入图片描述

网络状态判断

#!/bin/bash
IP=192.168.10.155
ping -c1 -w1 $IP &> /dev/null && echo "$IP is up" || { echo "$IP is unreachable"; exit; }
echo "Script is finished"

在这里插入图片描述

分区磁盘空间的判断

我这里的分区磁盘没有规划有“/dev/ma…”开头的,由“/dev/nv…”开头的,生成环境中应该规划为统一的名称。
在这里插入图片描述
磁盘空间利用率为5%时就报警并发送邮件

#!/bin/bash
WARNING=5
SPACE_USED=`df | grep '^/dev/ma' | grep -oE '[0-9]+%' | tr -d % | sort -nr | head -1`
[ "$SPACE_USED" -gt $WARNING ] && echo "DISK USED: $SPACE_USED%, will be full" | mail -s diskingwarning root #配置邮件发送

在这里插入图片描述

磁盘空间和Inode号的检测

在这里插入图片描述

#!/bin/bash
WARNING=5
SPACE_USED=`df | grep '^/dev/ma'|grep -oE '[0-9]+%' | tr -d % | sort -nr | head -1`
INODE__USED=`df -i | grep '^/dev/ma'|grep -oE '[0-9]+%' | tr -d % | sort -nr | head -1`
[ "$SPACE_USED" -gt $WARNING -o "$INODE_USED" -gt $WARNING ] && echo "DISK USED: $SPACE_USED%, INODE_USED: $INODE_USED, will be full" | mail -s "DISK Warning" Email address

read接受输入

read读取键盘输入

使用read来把输入值分配给一个或多个shell变量,read从标准输入中读取值,给每个单词分配一个变量,所有剩余单词都被分配给最后一个变量,如果变量名没有指定,默认标准输入的值赋值给系统内置变量REPLY。

[root@leiRocky ~]# read --help
read: read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] 
      [-t timeout] [-u fd] [name ...]
    Read a line from the standard input and split it into fields.
    
    Reads a single line from the standard input, or from file descriptor FD
    if the -u option is supplied.  The line is split into fields as with word
    splitting, and the first word is assigned to the first NAME, the second
    word to the second NAME, and so on, with any leftover words assigned to
    the last NAME.  Only the characters found in $IFS are recognized as word
    delimiters.
    
    If no NAMEs are supplied, the line read is stored in the REPLY variable.
    
    Options:
      -a array	assign the words read to sequential indices of the array
    		variable ARRAY, starting at zero
      -d delim	continue until the first character of DELIM is read, rather
    		than newline
      -e	use Readline to obtain the line in an interactive shell
      -i text	use TEXT as the initial text for Readline
      -n nchars	return after reading NCHARS characters rather than waiting
    		for a newline, but honor a delimiter if fewer than
    		NCHARS characters are read before the delimiter
      -N nchars	return only after reading exactly NCHARS characters, unless
    		EOF is encountered or read times out, ignoring any
    		delimiter
      -p prompt	output the string PROMPT without a trailing newline before
    		attempting to read
      -r	do not allow backslashes to escape any characters
      -s	do not echo input coming from a terminal
      -t timeout	time out and return failure if a complete line of
    		input is not read within TIMEOUT seconds.  The value of the
    		TMOUT variable is the default timeout.  TIMEOUT may be a
    		fractional number.  If TIMEOUT is 0, read returns
    		immediately, without trying to read any data, returning
    		success only if input is available on the specified
    		file descriptor.  The exit status is greater than 128
    		if the timeout is exceeded
      -u fd	read from file descriptor FD instead of the standard input
    
    Exit Status:
    The return code is zero, unless end-of-file is encountered, read times out
    (in which case it's greater than 128), a variable assignment error occurs,
    or an invalid file descriptor is supplied as the argument to -u.
-p        指定要显示的提示
-s        静默输入,一般用于密码
-n N      指定输入的字符长度N
-d '字符' 输入结束符
-t N     TIMEOUT为N秒

在这里插入图片描述
在这里插入图片描述

#!/bin/bash
read -p "Are you rich? yes or no: " ANSWER
[ $ANSWER = "yes" -o $ANSWER = "y" ] && echo "You are rich" || echo "Good Good Study,Day Day Up!"

在这里插入图片描述

实现运维菜单

work_menu.sh

#!/bin/bash
echo -en "\E[$[RANDOM%7+31];1m"
cat <<EOF
请选择:
1) 备份数据库
2) 清理日志
3) 软件升级
4) 软件回滚
5) 删库跑路
EOF
echo -en '\E[0m'
read -p "请选择上面项对应的数字1-5: " MENU
[ $MENU -eq 1 ] && ./backup.sh
[ $MENU -eq 2 ] && echo "清理日志"
[ $MENU -eq 3 ] && echo "软件升级"
[ $MENU -eq 4 ] && echo "软件回滚"
[ $MENU -eq 5 ] && echo "删库跑路"

backup.sh

#!/bin/bash
SRC=/etc
DEST=/data/backup_`date +%F_%H-%M-%S`
cp -a $SRC $DEST
[ $? -eq 0 ] && echo "backup.sh脚本执行完毕,备份完成!" || echo "备份失败"

在这里插入图片描述

read和管道符面试题

在这里插入图片描述
man bash
在这里插入图片描述
由此可知为什么x和y的值都没有赋值进去

1.由于管道中的每个命令运行在独立的子进程中;
2.所以"echo 1 2 |read x y; echo x=$x y=$y""echo 1 2 |read x y;"是子进程,"echo x=$x y=$y"是父进程;
3.父进程读取不了子进程中的变量;
4.因此可使用(){},将(read x y; echo x=$x y=$y)作为整体。

管道符开启了子进程
在这里插入图片描述

bash shell配置文件

bash shell的配置文件很多,可以按照生效范围和登录方式来分类。

1.按生效范围划分两类

全局配置

全局配置:针对所有用户有效

/etc/profile
/etc/profile.d/*.sh
/etc/bashrc

个人配置

个人配置:只针对特定用户有效

~/.bash_profile
~/.bashrc

以上配置文件都是在用户登录时,就会被读取到的配置文件。

2.shell登录两种方式分类

交互式登录

1.直接通过终端输入账号密码登录
2.使用su - userName切换的用户

配置文件生效和执行顺序: (以下的文件都是可执行的脚本文件)

放在每个文件最前
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
~/.bash_ profi1e
~/.bashrc
/etc/bashrc

放在每个文件最后
/etc/profile.d/*.sh
/etc/bashrc
/etc/profile
/etc/bashrc      #此文件执行两次
~/.bashrc
~/.bash_profile 

注意:文件之间的调用关系,写在同一个文件的不同位置,将影响文件的执行顺序。

source和bash命令的区别

我们知道,一个脚本在没有赋予执行权限时,是可以通过bash命令来执行的。实际上不仅bash命令可以执行脚本,source命令也可以执行脚本。source和点“.”又是等价的。

在这里插入图片描述
编写一个test.sh脚本,打印当前进程PID和父进程PID

#!/bin/bash
echo PID=$BASHPID
echo PPID=$PPID
sleep 100

在这里插入图片描述
bash test.sh
bash命令开启子进程,bash运行的脚本中的变量在影响当前子进程的环境。
在这里插入图片描述
source test.sh
source不会开启子进程,直接在当前进程中执行,也就意味着source执行的脚本中的变量会影响当前进程中的全局环境。
在这里插入图片描述
结论

bash和source的使用场景不一样,
1.bash用于普通脚本的执行,
2.source用于当前shell环境中的全局配置文件。

非交互式登录

1.su UserName
2.图形界面下打开的终端
3.执行脚本
4.任何其它的bash实例

执行顺序

/etc/profile.d/*.sh
/etc/bashrc
~/.bashrc

3.按功能划分分类

profile类和bashrc类
用户登录时才会触发的文件

Profile类

profile类为交互式登录的shell提供配置.

全局:  /etc/profile,  /etc/profile.d/*.sh
个人:  ~/.bash_profile

功用:
  (1)用于定义环境变量
  (2)运行命令或脚本

将来自己要写全局的配置文件就写在/etc/profile.d/里以.sh为后缀。这样可以放心修改自己的配置,不然容易改坏/etc/profile的配置,导致环境异常。可以看下/etc/profile文件里关于/etc/profile.d/*.sh读取的配置,如下

cat /etc/profile

for i in /etc/profile.d/*.sh /etc/profile.d/sh.local ; do
    if [ -r "$i" ]; then
        if [ "${-#*i}" != "$-" ]; then
            . "$i"
        else
            . "$i" >/dev/null
        fi
    fi
done

Bashrc类

bashrc类:为非交互式和交互式登录的shell提供配置

全局: /etc/bashrc
个人: ~/.bashrc

功用:
  (1)定义命令别名和函数
  (2)定义本地变量

编辑配置文件生效

修改profile和bashrc文件后需生效两种方法:

1.重新启动shell进程
2.source|.配置文件

注意:source 会在当前shell中执行脚本,所有一般只用于执行置文件,或在脚本中调用另一个脚本的场景

范例:

.  ~/.bashrc

Bash 退出任务

保存在 ~/.bash_logout 文件(此文件就是脚本)中(用户), 在退出登录shell时运行

功能:
  1.创建自动备份
  2.清除临时文件

在这里插入图片描述
vim .bash_logout

# ~/.bash_logout
rm -rf /data/backup*

退出终端时。删除/data目录下的所有backup开头的文件。
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值