Shell 脚本编程
📖简介:bash shell 编程入门,讲解什么是 shell、怎么写第一个脚本;shell 普通变量、环境变量、特殊位置变量;字符串子串截取;多种整数计算方式;test/
[]/[[]]条件测试语法。
一、有关 Shell 编程
1.1 什么是 Shell
Shell 是命令解释器,负责解析执行用户输入的命令。登录 Linux 之后,所有交互操作都交给 shell 处理。
- Linux 默认 shell:
bash(Bourne‑Again Shell) - Solaris/FreeBSD 默认:sh
- AIX 默认:ksh
类比:内核是操作系统核心,Shell 相当于包裹在内核外面的外壳,接收用户输入,转发给内核,再把内核输出展示给用户。
1.2 什么是 Shell 脚本
把多条命令写在文件中,批量执行,这种文件就是 shell 脚本。类似 Windows 的 bat 批处理。
- 交互模式:键盘敲一条,立刻执行一条
- 非交互模式:读取脚本文件,一次性批量执行全部语句
1.3 Shell 脚本的用途
Linux 几乎所有配置、日志、服务文件都是纯文本。shell 擅长处理文本,是运维自动化必备工具。
- 批量检查多台服务器运行状态
- 自动化部署、升级业务服务
- 日志分析、定期备份
学好 shell 需要的基础:vim 编辑器、Linux 常用命令、grep/sed/awk 三剑客、了解常见服务。
学习建议:多写多练,从简单脚本入手,多模仿,变量命名见名知意,形成自己编码习惯。
1.4 Shell 两大分支
- Bourne 家族:sh、ksh、bash(CentOS 默认)
- C‑Shell 家族:csh、tcsh,语法接近 C 语言,BSD/macOS 常见。
二、创建第一个 Shell 脚本
2.1 脚本头部幻数#!
脚本第一行指定解释器,称为幻数。
#!/bin/bash如果不写,系统会用当前 shell 执行;规范写法必须写第一行。
#代表注释,除了第一行#!,其余 #后面内容全部是注释,不会执行。
示例脚本hello.sh
#!/bin/bash # Date:2025‑10‑09 # Author:Created by user # Description:第一个shell测试脚本 # Version:1.0 echo "Hello World !"
/bin/sh实际是 bash 的软链接。
[user@shell ~]$ ls -l /bin/sh lrwxrwxrwx. 1 root root 4 11月 2 17:33 /bin/sh -> bash2.2 Shell 脚本 5 种执行方式
方式 1:bash/sh 直接执行,不需要 x 执行权限
[user@shell ~]$ bash hello.sh Hello World !方式 2:给脚本增加执行权限,相对 / 绝对路径运行
[user@shell ~]$ chmod +x hello.sh # 相对路径 [user@shell ~]$ ./hello.sh # 绝对路径 [user@shell ~]$ /home/user/hello.sh方式 3:source /.点号执行(在当前 shell 进程执行,不新建子 shell)
重点:source 执行脚本,脚本里面定义的变量会保留在当前 shell 环境。
[user@shell ~]$ source hello.sh # 等价写法 [user@shell ~]$ . hello.sh方式 4:管道、输入重定向执行
[user@shell ~]$ bash < hello.sh [user@shell ~]$ cat hello.sh | bash方式 5:放到 PATH 环境变量目录,直接脚本名运行
[user@shell ~]$ echo $PATH [user@shell ~]$ cp hello.sh ~/bin [user@shell ~]$ hello.sh2.3 Shell 脚本编写良好习惯
- 第一行写
#!/bin/bash指定解释器。 - 脚本开头增加版本、作者、描述注释,尽量使用英文注释,避免中文乱码。
- 成对符号
{} [] '' "" ``一次性写全,再填入内容,防止漏写。 - 中括号
[ ]内部两端必须保留空格[ 条件 ]。 - if、for、while、case 先把完整框架写出来,再填充业务代码。
- 使用缩进,提升可读性。
- 字符串赋值:简单字符串不加引号;带空格、变量解析用双引号;原样输出用单引号。
- 全部符号使用英文半角。
三、Shell 变量基础
变量:内存中临时存储数据,变量名由字母数字下划线组成,不能以数字开头。 bash 默认弱类型,不区分整数、字符串,全部当做字符串处理。
3.1 普通变量定义
三种赋值方式:
变量名=值:简单连续字符串变量名='值':单引号,所见即所得,不解析变量变量名="值":双引号,解析里面的变量、特殊符号
⚠️赋值等号左右绝对不能加空格。
[user@shell ~]$ a=192.168.1.2 [user@shell ~]$ b='192.168.1.2' [user@shell ~]$ c="192.168.1.2" [user@shell ~]$ echo "a=$a" a=192.168.1.2 # 单引号原样输出$符号,双引号解析变量 [user@shell ~]$ a=testval [user@shell ~]$ b='value is $a' [user@shell ~]$ c="value is $a" [user@shell ~]$ echo $b value is $a [user@shell ~]$ echo $c value is testval3.2 把命令输出赋值给变量
两种写法,推荐$(),反引号```容易和单引号混淆。
[user@shell ~]$ CMD1=$(pwd) [user@shell ~]$ echo $CMD1 /home/user [user@shell ~]$ CMD2=`date +%A` [user@shell ~]$ echo $CMD2 Thursday3.3 环境变量
环境变量可以传递给子 shell 进程;普通变量只在当前 shell 有效。 使用export导出为环境变量。
# 三种等价写法 export NAME=user NAME=user ; export NAME declare -x NAME=user查看变量命令:
set:输出所有变量,普通变量 + 环境变量env:只输出环境变量declare:输出变量、函数、整型
常用内置环境变量
表格
| 变量 | 说明 |
|---|---|
PATH | 命令搜索路径 |
HOME | 当前用户家目录 |
USER | 当前用户名 |
PWD | 当前工作目录 |
PS1 | 终端提示符 |
LANG | 系统字符集 |
HISTSIZE | history 历史命令条数 |
环境变量持久化配置文件
- 用户生效:
~/.bashrc(推荐)、~/.bash_profile - 全局所有用户:
/etc/bashrc、/etc/profile、/etc/profile.d/*.sh
登录 shell 和非登录 shell 加载顺序: 登录 shell:
/etc/profile→/etc/profile.d/*→~/.bash_profile→~/.bashrc→/etc/bashrc非登录 shell(ssh 非登录、执行脚本)只会加载~/.bashrc、/etc/bashrc。
unset 取消变量定义
[user@shell ~]$ unset NAME四、Shell 进阶变量
4.1 位置参数特殊变量
执行脚本时从命令行传入参数。
表格
| 变量 | 含义 |
|---|---|
$0 | 脚本本身文件名(带路径则显示路径) |
$1 $2 ... ${10} | 第 1、第 2… 第 10 个参数,大于 9 必须大括号${10} |
$# | 传入参数总个数 |
$* | 全部参数,不加引号和$@行为一致;"$*"把所有参数合并成一个字符串 |
$@ | 全部参数;"$@"保留每个参数独立,带空格参数也可以正确传递(推荐) |
示例脚本 showargs.sh
#!/bin/bash echo "\$0 = $0" echo "\$1 = $1" echo "\$2 = $2" echo "\${10}= ${10}" echo "\$# = $#" echo "\$* = $*" echo "\$@ = $@" [user@shell ~]$ bash showargs.sh a b c d e f g h i j k简单小脚本 ssh_ctl.sh
#!/bin/bash systemctl $1 sshd [user@shell ~]$ sudo bash ssh_ctl.sh stop [user@shell ~]$ sudo bash ssh_ctl.sh start [user@shell ~]$ sudo bash ssh_ctl.sh status4.2 状态特殊变量
表格
| 变量 | 说明 |
|---|---|
$? | 上一条命令退出返回值,0 成功,非 0 失败,最常用 |
$$ | 当前 shell 脚本 PID 进程号 |
$! | 上一个后台运行进程 PID |
$_ | 上一条命令最后一个参数 |
示例:
[user@shell ~]$ ls /etc/hosts [user@shell ~]$ echo $? 0 [user@shell ~]$ ls /root ls: 无法打开目录/root: 权限不够 [user@shell ~]$ echo $? 24.3 内置命令 read、shift、echo
echo 输出
-n:不换行输出-e:开启转义字符解析\n换行、\t制表符
[user@shell ~]$ echo -n "hello ";echo "world" hello world [user@shell ~]$ echo -e "hello\nworld" hello worldread 读取用户输入
# -p 提示文字,-s 不回显输入(密码) [user@shell ~]$ read -p "请输入内容:" str [user@shell ~]$ echo $str [user@shell ~]$ read -s -p "请输入密码:" pass [user@shell ~]$ echo $passshift 位置参数左移
每执行一次 shift,$2→$1、$3→$2,参数总数$#减一。 shift.sh
#!/bin/bash echo "第一个参数:$1" shift echo "shift之后第一个参数:$1" [user@shell ~]$ bash shift.sh apple banana orange 第一个参数:apple shift之后第一个参数:banana4.4 变量子串截取与替换
假设变量str="abc123abc123"
表格
| 语法 | 作用 |
|---|---|
${str} | 获取变量完整内容 |
${#str} | 获取字符串字符长度 |
${str:offset} | 从 offset 下标开始截取到末尾,下标从 0 开始 |
${str:offset:length} | 截取 offset 开始,length 个字符 |
${str#word} | 从开头删除最短匹配 word |
${str##word} | 从开头删除最长匹配 word |
${str%word} | 从结尾删除最短匹配 word |
${str%%word} | 从结尾删除最长匹配 word |
${str/pattern/rep} | 替换第一个匹配内容 |
${str//pattern/rep} | 全部全局替换 |
实操示例
[user@shell ~]$ str="abc123abc123" [user@shell ~]$ echo ${#str} 12 [user@shell ~]$ echo ${str:3} 123abc123 [user@shell ~]$ echo ${str:3:4} 123a # 替换示例 [user@shell ~]$ echo ${str/abc/DEF} DEF123abc123 [user@shell ~]$ echo ${str//abc/DEF} DEF123DEF1234.5 变量默认值扩展
表格
| 语法 | 行为 |
|---|---|
${var:-word} | 变量为空 / 未定义,返回 word;不修改原变量 |
${var:=word} | 变量为空 / 未定义,赋值 var=word 并返回 |
${var:?word} | 变量未定义,把 word 输出 stderr 报错退出 |
${var:+word} | 变量有值,返回 word;变量空则返回空 |
示例
[user@shell ~]$ unset test [user@shell ~]$ echo ${test:-"默认内容"} 默认内容 [user@shell ~]$ echo $test [user@shell ~]$ echo ${test:="hello"} hello [user@shell ~]$ echo $test hello生产小技巧,防止变量为空导致危险操作
find ${path:-/tmp} -name "*.tar.gz" -type f -mtime +7 | xargs rm -f五、Shell 数值计算
bash 原生只支持整数运算,不支持小数;小数使用
bc / awk。
5.1 双小括号(( ))
支持+ - * / % ** ++ --运算、比较判断。
[user@shell ~]$ echo $((1+1)) 2 [user@shell ~]$ a=5 [user@shell ~]$ ((a=a*2)) [user@shell ~]$ echo $a 10 # 自增 [user@shell ~]$ a=10 [user@shell ~]$ echo $((a++)) 10 [user@shell ~]$ echo $a 115.2 let 命令
等价(( )),用于变量运算赋值,等号两边不能空格
[user@shell ~]$ i=2 [user@shell ~]$ let i=i+8 [user@shell ~]$ echo $i 105.3 expr 命令
注意:运算符前后必须有空格,乘号
*需要反斜杠转义\*
[user@shell ~]$ expr 2 + 3 5 [user@shell ~]$ expr 3 \* 4 125.4 bc 计算器,支持小数运算
[user@shell ~]$ echo "scale=4; 1/3" | bc .33335.5 awk 做小数计算
[user@shell ~]$ echo "7.7 3.8" | awk '{print $1-$2}' 3.9六、条件测试判断(test / [] / [[]] / (( )))
shell 判断命令执行返回值:
0 = true 真,非 0 = false 假。
四种语法格式
test 表达式[ 表达式 ]👉最常用,注意中括号内部必须空格[[ 表达式 ]]👉 bash 扩展,支持&& || =~正则匹配(( ))👉 专门做数字比较,不需要空格
6.1 文件测试
表格
| 参数 | 含义 |
|---|---|
-d | 是否目录 |
-f | 是否普通文件 |
-e | 文件 / 目录是否存在 |
-r | 是否可读 |
-w | 是否可写 |
-x | 是否可执行 |
-L | 是否软链接 |
-s | 文件存在且大小不为 0 |
file1 -nt file2 | file1 修改时间比 file2 新 |
file1 -ot file2 | file1 修改时间比 file2 旧 |
示例:
[user@shell ~]$ [ -d /tmp ] && echo "目录存在" 目录存在 [user@shell ~]$ [ -f /etc/passwd ] && echo "普通文件" 普通文件6.2 字符串测试
表格
| 参数 | 含义 |
|---|---|
-z "$var" | 字符串长度为 0(空)返回真 |
-n "$var" | 字符串不为空返回真 |
"a" = "b" | 字符串相等 |
"a" != "b" | 字符串不相等 |
⚠️变量建议双引号包裹,防止变量为空出现语法报错。
[user@shell ~]$ name="" [user@shell ~]$ [ -z "$name" ] && echo "字符串为空" 字符串为空 [user@shell ~]$ str="hello" [user@shell ~]$ [ "$str" != "world" ] && echo "不相等" 不相等[[ ]]支持正则匹配=~,正则不要加引号
[user@shell ~]$ num="12345" [user@shell ~]$ [[ "$num" =~ ^[0-9]+$ ]] && echo "全数字" 全数字6.3 整数比较
[]`使用 `-eq -ne -gt -ge -lt -le` `(( ))`、`[[]]`可以直接用 `> < >= <= == !=
表格
| 符号 [] 中 | 含义 |
|---|---|
-eq | 等于 equal |
-ne | 不等于 not equal |
-gt | 大于 greater than |
-ge | 大于等于 greater equal |
-lt | 小于 less than |
-le | 小于等于 less equal |
示例
[user@shell ~]$ [ 10 -gt 5 ] && echo "10大于5" 10大于5 [user@shell ~]$ (( 10 > 5 )) && echo "10大于5" 10大于56.4 逻辑运算符
- 在
[ ] / test内部:-a逻辑与;-o逻辑或;!逻辑非 - 在
[[ ]] / (( ))内部:&&与;||或;!非 - 表达式之间:
&&前面成功才执行后面;||前面失败才执行后面
# [] 内部逻辑与 [user@shell ~]$ num=12 [user@shell ~]$ [ $num -gt 10 -a $num -lt 20 ] && echo "10<num<20" 10<num<20 # [[]] && [user@shell ~]$ [[ $num -gt 10 && $num -lt 20 ]] && echo "10<num<20" 10<num<20多命令块,条件成立执行多条语句,使用
{ 命令1;命令2; },注意大括号内侧前后空格、分号结尾。
[user@shell ~]$ [ -d /tmp/test ] || { mkdir -p /tmp/test echo "创建目录完成" }📝小结
- shell 脚本第一行写
#!/bin/bash幻数;执行脚本区分新建子 shell(bash ./xxx.sh)与 source(当前 shell 执行,保留变量)。 - 变量:双引号解析变量;单引号原样输出;
$()捕获命令输出。环境变量用 export 导出,区分登录 shell / 非登录 shell 配置文件。 - 特殊变量:
$0 $1 $# $* $@ $? $$ $!,"$@"是传递多参数最佳实践。 - 字符串子串
${#var}取长度,${var:offset:length}截取,${var/pat/rep}替换;还有变量默认值扩展${var:-}。 - 整数运算优先
(( ));小数使用 bc/awk;expr 注意运算符空格与转义。 - 条件测试:
[ ]通用,内部必须空格,整数用‑eq‑gt‑lt;[[ ]]支持&& || =~正则;(( ))专门数字比较,直接> < ==。