目录
[if...else 语句](#if...else 语句)
[重定向:> (覆盖) 与 >> (追加)](#重定向:> (覆盖) 与 >> (追加))
[重定向错误输出:2> / 2>>](#重定向错误输出:2> / 2>>)
[同时重定向 stdout 与 stderr](#同时重定向 stdout 与 stderr)
[POSIX 推荐(经常使用)](#POSIX 推荐(经常使用))
[将输出丢弃 /dev/null](#将输出丢弃 /dev/null)
前言
在 Linux 世界里,"一切皆文件",而 Shell 则是我们与这些文件、进程、系统资源沟通的桥梁。判断字符串是否相等、检测端口是否被监听、验证文件权限、重定向输出与错误------这些看似琐碎的操作,恰恰构成了日常运维、自动化部署、故障排查的基石。本篇文章通过大量可直接复制运行的示例,把"字符串比较、文件测试、条件分支、重定向与文件描述符"这些高频知识点串成一条清晰的主线:
-
从最简单的
[ "$a" = "$b" ]
开始,理解测试命令的"真/假"本质; -
用
-f/-d/-r/-w/-x/-s/-e
等选项快速判断文件类型和权限; -
通过单分支、双分支、多分支的
if/elif/else
脚本,把零散的测试组合成有逻辑的业务流程; -
进一步用
>/>>/2>/2>>/&>/&>>
掌握标准输出(FD 1)与标准错误(FD 2)的重定向技巧; -
最终学会把"条件判断 + 重定向"综合运用到端口检测、网络探活、服务安装等真实场景。
字符串
-n 判断是否为非空
-z 判断是否为空
= 判断是否相等
!= 判断是否不相等
示例:判断两个是否相等
[root@localhost shelldemo]# a=123
[root@localhost shelldemo]# b=123
[root@localhost shelldemo]# [ $a = $b ] && echo "两个字符串相等"
两个字符串相等
[root@localhost shelldemo]# [ $a != $b ] && echo "两个字符串不相等"
示例
shell脚本
str="hello"
if [ -n "$str" ];then
echo "字符串为非空"
else
echo "字符串为空"
fi
运行
[root@localhost shelldemo]# sh hello14.sh
字符串为非空
文件
文件测试运算符 运算符号
测试选项 | 含义(为真时) | 用法示例 |
---|---|---|
-f |
存在且是普通文件 | [ -f 文件路径 ] |
-d |
存在且是目录 | [ -d 目录路径 ] |
-s |
文件存在且大小不为 0 | [ -s 文件路径 ] |
-e |
文件(或目录)存在 | [ -e 文件路径 ] |
-r |
文件存在且当前用户可读 | [ -r 文件路径 ] |
-w |
文件存在且当前用户可写 | [ -w 文件路径 ] |
-x |
文件存在且当前用户可执行/可搜索 | [ -x 文件路径 ] |
# 判断某一个目录是否可读可写
[root@localhost shelldemo]# [ -d /etc ] && [ -r /etc ] && echo "可读目录"
可读目录
# 判断该系统版本是否为英文版
[root@localhost shelldemo]# [ $LANG != "en.US" ] && echo "Not en.US"
Not en.US
# 判断当前用户是否小于等于5
[root@localhost shelldemo]# [ $(who|wc -l) -le 5 ] && echo "用户太少"
用户太少
[root@localhost shelldemo]# [ $(who|wc -l) < 5 ] && echo "用户太少"
用户太少
if...else 语句
单支:
bash
if [ 条件 ]; then
命令
fi
示例
# shell脚本
if [ -s /etc ];then
echo "文件不为空"
fi
# 运行
[root@localhost shelldemo]# sh hello16.sh
文件不为空
双支:
bash
if [ 条件 ]; then
命令1
else
命令2
fi
示例1
# shell脚本
if [ $UID -eq 0 ]; then
echo "当前用户是管理员"
else
echo "当前用户不是管理员"
fi
# 运行
[root@localhost shelldemo]# sh hello17.sh
当前用户是管理员
示例2
# shell脚本
num1=$1
num2=$2
if [ $num1 -gt $num2 ]; then
echo "$num1大于$num2"
else
echo "$num1小于$num2"
fi
# 运行
[root@localhost shelldemo]# sh hello18.sh 1234 456
1234大于456
[root@localhost shelldemo]# sh hello18.sh 765 1234567
765小于1234567
示例
# shell脚本
if netstat -antulp|grep ":80" > /dev/null 2>&1; then
echo "80端口运行"
else
if [ $? -eq 0 ]; then
echo "web网站服务已经运行"
else
echo "启动http服务"
yum install -y httpd > /dev/null
systemctl restart httpd
fi
fi
# 注:/dev/null 2>&1 将错误日志传输,使得它往后面的if语句运行,yum云安装
# 运行
[root@localhost shelldemo]# sh hello19.sh
启动http服务
file:///mnt/repodata/repomd.xml: [Errno 14] curl#37 - "Couldn't open file /mnt/repodata/repomd.xml"
正在尝试其它镜像。
示例
# shell脚本
ip=$1
ping -c 2 -i 0.2 -W 3 $ip &> /dev/null
#-c 次数 -i ping一次多少秒 -W 反馈结果的时间
if [ $? -eq 0 ]; then
echo "$ip is up"
else
echo "$ip is down"
fi
# 运行
[root@localhost shelldemo]# sh hello20.sh 192.168.10.106
192.168.10.106 is up
[root@localhost shelldemo]# sh hello20.sh 192.168.10.105
192.168.10.105 is down
示例:判断httpd是否安装
# shell脚本
rpm -q httpd >rpm.txt 2>&1
if [ $? -eq 0 ]; then
echo "安装httpd"
else
echo "没安装httpd"
fi
# 测试
[root@zardcopy Y]# sh dome1.sh
安装httpd
示例:需求:检查用户是否存在 如果不存在就创建新用户,并且设置初始密码为"123456"
# shell脚本
read -p "请输入当前用户名:" username
cat /etc/passwd|grep $username
if [ $? -eq 0 ]; then
echo "用户已创建"
else
echo "用户没有创建"
useradd $username
echo "123" | passwd --stdin $username
fi
# 测试
[root@localhost shelldemo]# sh dome7.sh
请输入当前用户名:test4
用户没有创建
更改用户 test4 的密码 。
passwd:所有的身份验证令牌已经成功更新。
[root@localhost shelldemo]# su - test4
[test4@localhost ~]$ su - test4
密码:
上一次登录:六 8月 30 09:41:46 CST 2025pts/2 上
[test4@localhost ~]$ su - root
密码:
上一次登录:六 8月 30 09:26:23 CST 2025从 192.168.10.90pts/2 上
多支:
if [ 条件1 ]; then
命令1
elif [ 条件2 ]; then
命令2
...
else
默认命令
fi
示例:判断分数等级
bash
#!/bin/bash
score=$1
if [ $score -ge 90 ]; then
echo "优秀"
elif [ $score -ge 80 ]; then
echo "良好"
elif [ $score -ge 60 ]; then
echo "及格"
else
echo "不及格"
fi
# 运行
[root@localhost shelldemo]# sh hello10.sh 98
优秀
示例:写一个脚本,判断文件类型 d f b
# shell脚本
read -p "请输入路径:" name
if [ -d $name ]; then
echo "这是一个文件夹"
elif [ -f $name ]; then
echo "这是一个文件"
elif [ -b $name ]; then
echo "这是一个设备文件"
else
echo "无法判断"
fi
# 输出结果
[root@localhost shelldemo]# sh hello21.sh
请输入路径:/root
这是一个文件夹
[root@localhost shelldemo]# sh hello21.sh
请输入路径:/root/shelldemo/hello21.sh
这是一个文件
示例:
# shell脚本
# 用date命令记录当前的小时
a=$(date +%H)
# if判断
if [ $a -ge 6 -a $a -lt 11 ]; then
echo "现在是上午$a点"
elif [ $a -ge 11 -a $a -lt 13 ]; then
echo "现在是中午$a点"
elif [ $a -ge 13 -a $a -lt 18 ]; then
echo "现在是下午$a点"
elif [ $a -ge 18 -a $a -lt 24 ]; then
echo "现在是晚上$a点"
else
echo "现在是凌晨$a点"
fi
# 测试
[root@localhost shelldemo]# sh dome21.sh
现在是上午08点
[root@localhost shelldemo]# date -s "2025-08-30 12:00:00"
2025年 08月 30日 星期六 12:00:00 CST
[root@localhost shelldemo]# sh dome21.sh
现在是中午12点
[root@localhost shelldemo]# date -s "2025-08-30 16:00:00"
2025年 08月 30日 星期六 16:00:00 CST
[root@localhost shelldemo]# sh dome21.sh
现在是下午16点
标准文件描述符(FD)
0 = stdin (标准输入)
1 = stdout (标准输出)
2 = stderr (标准错误)
示例(分别演示 stdout/stderr ):
[root@localhost shelldemo]# echo "out"
out # 到 stdout
[root@localhost shelldemo]# echo "err" >&2
err # 到stderr
重定向:> (覆盖) 与 >> (追加)
> 把 stdout 写到文件(覆盖原内容)
>> 把 stdout 追加到文件末尾
[root@localhost shelldemo]# echo "first" > file.txt
[root@localhost shelldemo]# echo "second" > file.txt
[root@localhost shelldemo]# cat file.txt #覆盖
second
[root@localhost shelldemo]# echo "third" >> file.txt
[root@localhost shelldemo]# cat file.txt #追加
second
third
**重定向错误输出:2>/**2>>
[root@localhost shelldemo]# ls /no/such/path 2>err.txt #重覆盖
[root@localhost shelldemo]# cat err.txt
ls: 无法访问/no/such/path: 没有那个文件或目录
[root@localhost shelldemo]# ls /no/same/file 2>>err.txt #追加
[root@localhost shelldemo]# cat err.txt
ls: 无法访问/no/such/path: 没有那个文件或目录
ls: 无法访问/no/same/file: 没有那个文件或目录
同时重定向stdout与****stderr
有几种常用写法(要注意兼容性与顺序):
POSIX****推荐(经常使用)
command >out.txt 2>&1
含义:先把 stdout 重定向到 out.txt ,再把 stderr(2)重定向到当前 stdout(1)指向的位置(也就
是 out.txt )。结果: stdout 和 stderr 都写入 out.txt 。
# 先传正确,再传错误
[root@localhost shelldemo]# bash -c 'echo out; echo err >&2' >both.txt 2>&1
[root@localhost shelldemo]# cat both.txt
out
err
# 仅仅把错误的传过去
[root@localhost shelldemo]# bash -c 'echo out; echo err >&2' 2>&1 >both.txt
err
将输出丢弃/dev/null
含义 :把不想要的输出重定向到 /dev/null (黑洞)。
示例:只保留 stderr ,丢弃 stdout :
$ some_command > /dev/null
丢弃 stderr :
$ some_command 2> /dev/null
同时丢弃两者:
$ some_command > /dev/null 2>&1
# 或(bash):
$ some_command &> /dev/null
总结
-
判断的本质:任何测试命令(
[ ]
、[[ ]]
、(( ))
、命令本身)都会返回"退出状态码"------0 为真,非 0 为假;脚本只需根据这个状态码决定下一步动作。 -
文件与字符串测试:记住常用选项(
-f/-d/-e/-r/-w/-x/-s/-n/-z
),就能在一行内完成"存在性、类型、权限、空值"四大类检查。 -
条件结构:
-
单分支
if ... then ... fi
------ 满足就执行; -
双分支
if ... then ... else ... fi
------ 二选一; -
多分支
if ... elif ... else ... fi
------ 多选一。
-
-
重定向:
-
>
覆盖、>>
追加、2>
定向错误、&>
合并全部; -
/dev/null
是"黑洞",任何不想保留的输出都可以丢进去; -
顺序决定结果:
>file 2>&1
合并输出与错误到 file;2>&1 >file
则只把错误合并到原 stdout。
-
-
实战套路:
-
端口检测:
netstat -antulp | grep -q ":80"
; -
网络探活:
ping -c 2 -W 3 $ip &>/dev/null && echo up || echo down
; -
服务安装:先端口检测 → 不存在就
yum/dnf install -y httpd && systemctl enable --now httpd
。
-
把这些基本积木拼接起来,你就能写出既健壮又易维护的自动化脚本,真正做到"让 Shell 替你搬砖,而你专注思考"。