shell编程之条件测试和判断

本文详细介绍了Shell脚本的基础知识,包括算术运算、条件测试、参数传递和if条件判断等内容。深入探讨了算术运算的不同方法,条件测试的各种表达式,参数传递的技巧,以及if条件判断的应用实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Shellcheck一套用来检查和分析Shell脚本的自动化检查工具。除了检查撰写样式之外,它还能够识别Shell脚本里有什么指令可能存在执行风险,并提供对应的修改方式协助改善Shell脚本的质量。

算术运算

下图来自《跟老男孩学Linux运维:Shell编程实战》

在这里插入图片描述
推荐使用$((expression)) 进行算术运算,letexpr略过。
使用逻辑运算符的时候,返回1表示true,返回0表示false`。

#!/usr/bin/env bash
# bash 中等号两端不能有空格
# let var_name=expression
let sum=1+2
echo ${sum}
# $[expression]
sum=$[ 1 + 2 ]
echo ${sum}
# $((expression))
sum=$(( 1 + 2 ))
echo ${sum}
# $(expr arg1 arg2 arg3 ...)
sum=$(expr 1 + 2 + 4)
echo ${sum}

示例1:

#!/usr/bin/env bash
# 计算 /etc/passwd中第5和第10个用户的 id 号之和
user_id1=$(id -u "$(head -5 /etc/passwd | tail -1 | cut -d: -f1)")
user_id2=$(id -u "$(head -10 /etc/passwd | tail -1 | cut -d: -f1)")
echo "$((user_id1 + user_id2))"

示例2

#!/usr/bin/env bash
# 计算 /etc/profile 和 ~/.bashrc 的空白行数之和
profile_blank_num=$(grep -c "^[[:space:]]*$" /etc/profile);
bashrc_blank_num=$(grep -c "^[[:space:]]*$" ~/.bashrc);

echo "$((profile_blank_num + bashrc_blank_num))"

条件测试

条件测试的两种方式

  1. 根据命令的状态返回值来判断,返回值为0表示真,其他的情况为假

    #!/usr/bin/env bash
    # 判断 root 用户是否登录,如果 root 用户已经登录,则执行后面的 echo语句
    who | grep "^root\>" > /dev/null && echo "root账号已经登录"
    
  2. 使用测试表达式

    1. test expression

    2. [ expression ] 和上面的test基本等价 ,注意expression前后都要和中括号保留一个空格。

    3. [[ expression ]] 中可以使用通配符等进行模式匹配,也可以使用&&||><等逻辑运算符,这这是不同于[]的地方,在[]进行关系运算,一般使用-a-o等代替。

    4. 数值关系运算,使用 ((number_expression)) ,返回1表示true,返回0表示false

      下图来自《跟老男孩学Linux运维:Shell编程实战》

      在这里插入图片描述

测试表达式的写法

数值测试表达式

用于比较数值的大小。

运算符示例作用
-eq2 -eq 3判断两个数值是相等
-gt2 -gt 3判断 2 是否大于 3
-ge2 -ge 3判断 2 是否大于等于 3
-lt2 -lt 3判断 2 是否小于 3
-le2 -le 3判断 2 是否小于等于 3

字符串测试表达式

字符串测试表达式中,如果包含变量比较,需要将变量用双引号引起来,防止因为变量不存在导致的脚本报错。

另外,字符串测试表达式尽可能使用双中括号的形式,如[[ str1 == str2 ]]

运算符示例作用
==str1 == str2判断 str1 和 str2 是否相等
!=str1 != str2判断 str1 和 str2 是否不相等
>str1 > str2判断 str1 是否大于 str2
<str1 < str2判定 str1 是否小于 str2
=~str1 =~ “1$”判断 str1 是否匹配右侧的 pattern 模式
-z-z str1判断 str1 是否为空字符串
-n-n str1判断 str1 是否为非空字符串

文件测试表达式

存在性测试

测试 FILE 表达式是否存在。

  1. -a FILE
  2. -e FILE

存在性及类型测试

运算符示例作用
-b-b FILE判断 FILE 是否存在且为 块设备
-c-c FILE判断 FILE 是否存在且为 字符设备
-d-d FILE判断 FILE 是否存在且为 目录文件
-f-f FILE判断 FILE 是否存在且为 普通文件
-h/-l-h FILE 或者 -l FILE判断 FILE是否存在且为符号链接文件
-p-p FILE判断 FILE 是否存在且为 管道文件
-S-S FILE判断 FILE 是否存在且为 套接字文件

文件权限测试表达式

运算符示例作用
-r-r FILE判断 FILE 是否存在且可读
-w-w FILE判断 FILE 是否存在且可写
-x-x FILE判断 FILE 是否存在且可执行

特殊权限测试表达式

运算符示例作用
-g-g FILE判断 FILE 是否存在且拥有 sgid 权限
-u-u FILE判断 FILE 是否存在且拥有 suid 权限
-k-k FILE判断 FILE 是否存在且拥有 sticky 权限

文件非空测试表达式

运算符示例作用
-s-s FILE判断 FILE 是否存在且有内容(非空)

时间戳测试表达式

运算符示例作用
-N-N FILE判断 FILE 文件自从上一次被读取操作后是否被修改过

从属关系测试表达式

运算符示例作用
-O-O FILE判断 FILE 是否存在且当前用户为属主
-G-G FILE判断 FILE 是否存在且当前用户为属组

双目测试表达式

运算符示例作用
-efFILE1 -ef FILE2判断 FILE1 和 FILE2 是否指向同一个文件系统的相同 inode 的硬链接
-otFIFE1 -ot FILE2判断 FILE1 是否旧于 FILE2
-ntFIFE1 -nt FILE2判断 FILE1 是否新于 FILE2

组合测试条件

逻辑条件方式一方式二
逻辑与命令:COMMAND1 && COMMAND2
测试表达式 [ expression1 ] && [ expression2 ]
命令:不支持
测试表达式 [ expression1 -a expression2 ]
逻辑或命令:COMMAND1 || COMMAND2
测试表达式 [ expression1 ] || [ expression2 ]
命令:不支持
测试表达式 [ expression1 -o expression2 ]
逻辑非命令:! COMMAND
测试表达式 ! [ expression ] 或者[ ! expresstion ]
注意!后要有一个空格
命令:! COMMAND
测试表达式! [ expression ] 或者[ ! expresstion ]
注意!后要有一个空格

方式二之所以是不支持命名模式,是因为命令是不能放在中括号中执行的,了解即可。推荐方式一的写法。

参数传递

传递给脚本的参数依次放置在$1$2$3$4…的变量中,如果参数的个数大于9,则使用${n}

#!/usr/bin/env bash

# 接收两个数字,计算他们的和,并输出
echo $(( $1+$2 ))

bash demo.sh 3 5 打印出 8

参数轮替,又叫位置参数轮替。shift [n]n 表示每次操作被剔除去的参数个数,有点像先进先出,通过shift不断将先进的参数弹出。

#!/usr/bin/env bash
# 有参数 tom  jack  askou joy
# 参数轮替
echo "$1"  # tom
shift 2
echo "$1"   # askou
shift 1
echo "$1"  # joy

执行脚本

bash demo.sh /etc/profile ~/.bashrc
# 文件 /etc/profile 的空白行数为 13
# 文件 /home/xingmu/.bashrc 的空白行数为 34
# 两个文件的空白行之和为 47

特殊变量

变量作用
$0指向脚本执行时候的名称/脚本路径
$#脚本收到的参数个数
$?命令/脚本的执行状态
$*比如传递了 5 个参数,那么对于"$*"来说,这 5 个参数会合并到一起形成一份数据,它们之间是无法分割的;
$@对于"$@"来说,这 5 个参数是相互独立的,它们是 5 份数据。可以使用循环依次输出

如果使用 echo 直接输出"$*""$@"做对比,是看不出区别的;但如果使用 for 循环来逐个输出数据,立即就能看出区别来。

脚本状态返回值默认是脚本最后一条命令的返回值,也可以自定义状态值,使用exit [n] 返回,n就是自定义的状态码,自定义状态码也要遵守正常返回状态码0,其他情况下返回1-255的任意数值。

需要明白的是,shell遇到exit指令就会结束执行,同时返回状态码。

if 条件判断

示例1

#!/usr/bin/env bash

# 变量 hostName 是否为空、localhost或者localhost.localdomain,如果是则输出 expression.test.com,否则输出 host

hostName="localhost.localdomain"

if echo $hostName | grep -qE "^[[:space:]]$|localhost(\.localdomain)?$"; then
  echo "change hostname to expression.test.com"
else
  echo "hostname is $hostName"
fi

示例2: 判断给定用户是否存在,如果存在给出提示,如果不存在,则添加用户

#!/usr/bin/env bash

# 判断给定用户是否存在,如果存在给出提示,如果不存在,则添加用户

if [ $# -lt 1 ]; then
  echo "请输入用户名"
elif ! echo "$1" | grep -qE "^[[:alnum:]]+$"; then
  echo "无效用户名"
else
  if grep -Eq "^$1\>" /etc/passwd; then
    echo "用户 $1 已存在"
  else
    sudo useradd "$1"
    echo "用户 $1 已经添加成功"
  fi
fi

示例3 : 通过命令传递两个文件的路径,计算其空白行数之和

#!/usr/bin/env bash
# 通过命令传递两个文件的路径,计算其空白行数之和
if [ ! -f "$1" ] || [ ! -f "$2" ]; then
   echo "参数必须为已经存在的普通文件"
   exit 1
fi


first_second_blank_line=$(grep -Ec "^[[:space:]]*$" "$1")
echo "文件 $1 的空白行数为 $first_second_blank_line"
second_blank_line=$(grep -Ec "^[[:space:]]*$" "$2")
echo "文件 $2 的空白行数为 $second_blank_line"

echo "两个文件的空白行之和为 $(( first_second_blank_line + second_blank_line))"

示例4 :判断给定用户名的 id 号是奇数还是偶数

#!/usr/bin/env bash

# 判断给定用户的 id 号是奇数还是偶数
if ! grep -Eq "^$1\>" /etc/passwd; then
  echo "用户 $1 不存在"
  exit 1
fi

user_id=$(id -u $1)
echo "用户 $1 的id为 $user_id"
if [ $((user_id % 2)) -eq 0 ]; then
  echo "用户 $1 的id是偶数"
else
  echo "用户 $1 的id是奇数"
fi

示例5 : 使用算术的逻辑运算重写示例4,表达式就变得简单了

#!/usr/bin/env bash
# 判断给定用户的 id 号是奇数还是偶数
if ! grep -Eq "^$1\>" /etc/passwd; then
  echo "用户 $1 不存在"
  exit 1
fi

user_id=$(id -u $1)
echo "用户 $1 的id为 $user_id"
if ((user_id % 2==0)); then
  echo "用户 $1 的id是偶数"
else
  echo "用户 $1 的id是奇数"
fi

  1. a-z ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值