Shell 编程入门指南:从基础到实战2

目录

前言

字符串

[if...else 语句](#if...else 语句)

单支:

双支:

多支:

标准文件描述符(FD)

[重定向:> (覆盖) 与 >> (追加)](#重定向:> (覆盖) 与 >> (追加))

[重定向错误输出: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

总结

  1. 判断的本质:任何测试命令([ ][[ ]](( ))、命令本身)都会返回"退出状态码"------0 为真,非 0 为假;脚本只需根据这个状态码决定下一步动作。

  2. 文件与字符串测试:记住常用选项(-f/-d/-e/-r/-w/-x/-s/-n/-z),就能在一行内完成"存在性、类型、权限、空值"四大类检查。

  3. 条件结构:

    • 单分支 if ... then ... fi ------ 满足就执行;

    • 双分支 if ... then ... else ... fi ------ 二选一;

    • 多分支 if ... elif ... else ... fi ------ 多选一。

  4. 重定向:

    • > 覆盖、>> 追加、2> 定向错误、&> 合并全部;

    • /dev/null 是"黑洞",任何不想保留的输出都可以丢进去;

    • 顺序决定结果:>file 2>&1 合并输出与错误到 file;2>&1 >file 则只把错误合并到原 stdout。

  5. 实战套路:

    • 端口检测: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 替你搬砖,而你专注思考"。

相关推荐
前端大卫6 小时前
Vue 和 React 受控组件的区别!
前端
Hy行者勇哥7 小时前
前端代码结构详解
前端
练习时长一年7 小时前
Spring代理的特点
java·前端·spring
水星记_7 小时前
时间轴组件开发:实现灵活的时间范围选择
前端·vue
2501_930124708 小时前
Linux之Shell编程(三)流程控制
linux·前端·chrome
潘小安8 小时前
『译』React useEffect:早知道这些调试技巧就好了
前端·react.js·面试
@大迁世界8 小时前
告别 React 中丑陋的导入路径,借助 Vite 的魔法
前端·javascript·react.js·前端框架·ecmascript
EndingCoder9 小时前
Electron Fiddle:快速实验与原型开发
前端·javascript·electron·前端框架
EndingCoder9 小时前
Electron 进程模型:主进程与渲染进程详解
前端·javascript·electron·前端框架