4.Shell脚本修炼手册---变量进阶知识
4. Shell 变量进阶知识
文章目录
- 4. Shell 变量进阶知识
- 4.1 Shell 中的特殊变量
- 4.1.1 位置参数变量:获取脚本的输入参数
- 4.1.2 进程状态变量:获取命令执行的状态信息
- 4.2 Shell 内置命令
- 4.2.1 `echo`:输出内容到终端*
- 4.2.2 `read`:从终端读取输入
- 4.2.3 `exec`:替换当前进程执行命令
- 4.3 Shell 变量子串:截取和处理变量内容
- 4.3.1 常用变量子串语法
- 4.3.2 变量子串实践示例
- 4.3.3 实验应用案例
4.1 Shell 中的特殊变量
在 Shell 脚本中,有一些自带的 “特殊变量”,它们不需要我们手动定义,却能帮我们快速获取脚本运行时的关键信息(比如参数、进程状态等)。下面我们逐个认识这些变量,以及它们的具体用法。
4.1.1 位置参数变量:获取脚本的输入参数
当我们运行一个 Shell 脚本时,经常需要给它传递一些参数(比如 bash script.sh 参数1 参数2
)。位置参数变量就是用来获取这些参数的工具,常见的有以下几个:
变量 | 作用说明 |
---|---|
$0 | 获取当前执行的 Shell 脚本的文件名(如果执行时带路径,会包含完整路径) |
$n | 获取第 n 个参数(n 是数字,比如 $1 是第 1 个参数;n>9 时需用 ${10} ) |
$# | 获取传递给脚本的参数总个数 |
$* | 获取所有参数(不加引号时和 $@ 一样;加引号 "$*" 时,所有参数会被合并成一个字符串) |
$@ | 获取所有参数(不加引号时和 $* 一样;加引号 "$@" 时,每个参数保持独立字符串) |
实验:位置参数变量的使用演示 实验流程:创建一个脚本 showargs.sh
,在脚本中打印各种位置参数变量,然后传递参数执行脚本,观察输出结果。
步骤 1:创建脚本并添加内容
[bq@shell ~]$ vim showargs.sh
#!/bin/bash
# 打印脚本文件名($0)
echo "脚本文件名:$0"
# 打印第1个参数($1)
echo "第1个参数:$1"
# 打印第2个参数($2),(到$9前以此类推)
echo "第2个参数:$2"
# 错误示范:n>9 时直接用 $10 会被解析为 $1+0
echo "错误的第10个参数(\$10):$10"
# 正确示范:n>9 时需用 ${10}
echo "正确的第10个参数(\${10}):${10}"
# 打印参数总个数($#)
echo "参数总个数:$#"
# 打印所有参数(不加引号的 $*)
echo "所有参数(\$*):$*"
# 打印所有参数(不加引号的 $@)
echo "所有参数(\$@):$@"
# 打印所有参数(加引号的 "$@",保持参数独立性)
echo "所有参数(\"$@\"):$@"
步骤 2:执行脚本并传递参数 我们传递 a到z
共 26 个参数(用 {a..z}
快速生成):
[bq@shell ~]$ bash showargs.sh {a..z}
步骤 3:观察输出并理解含义
脚本文件名:showargs.sh # $0 输出文件名
第1个参数:a # $1 输出第1个参数a
第2个参数:b # $2 输出第2个参数b
错误的第10个参数($10):a0 # $10 被解析为 $1(a)+0,所以是a0
正确的第10个参数(${10}):j # ${10} 正确获取第10个参数j(a是第1个,j是第10个)
参数总个数:26 # $# 输出参数总数26
所有参数($*):a b c d ... z # 所有参数用空格分隔
所有参数($@):a b c d ... z # 不加引号时和 $* 一致
所有参数("$@"):a b c d ... z # 加引号时保持每个参数独立
实际应用案例:简化命令执行 比如我们可以写一个脚本 ssh_ctl.sh
,通过传递参数控制 sshd
服务的启动 / 停止 / 状态:
#!/bin/bash
# 接收第1个参数($1)作为操作命令(start/stop/status),传递给 systemctl 控制 sshd
systemctl $1 sshd
执行效果:
# 停止 sshd 服务($1 为 stop)
[bq@shell ~]$ sudo bash ssh_ctl.sh stop
# 查看 sshd 状态($1 为 status)
[bq@shell ~]$ sudo bash ssh_ctl.sh status
# 启动 sshd 服务($1 为 start)
[bq@shell ~]$ sudo bash ssh_ctl.sh start
4.1.2 进程状态变量:获取命令执行的状态信息
除了输入参数,我们还经常需要知道 “上一个命令是否执行成功”“当前脚本的进程号” 等信息,这就需要用到进程状态变量。
变量 | 作用说明 |
---|---|
$? | 获取上一个命令的执行结果(0 表示成功,非 0 表示失败,最常用) |
$$ | 获取当前脚本 / Shell 的进程号(PID) |
$! | 获取上一个后台运行进程的 PID |
$_ | 获取上一个命令中最后一个参数 |
1. $?
:判断命令是否执行成功 $?
是最常用的进程状态变量,它的返回值规则是:
- 0:上一个命令执行成功
- 非 0:上一个命令执行失败(具体数值代表不同错误,可通过
man 命令
查看说明)
示例验证:
# 执行成功的命令(ls 查看存在的目录)
[bq@shell ~]$ ls hello # 假设 hello 是存在的目录
hello
[bq@shell ~]$ echo $? # 输出 0,代表成功
0# 执行失败的命令(ls 查看无权限的目录)
[bq@shell ~]$ ls /root # 普通用户无权限访问 /root
ls: 无法打开目录/root: 权限不够
[bq@shell ~]$ echo $? # 输出 2,代表失败(通过 man ls 可知 2 是“严重错误”)
2
通过 man ls
可以查看 ls
命令的退出码含义:
Exit status:0 表示成功1 表示轻微问题(如无法访问子目录)2 表示严重问题(如无法访问命令行参数指定的路径)
**2. `
(:获取当前进程的 PID**\
)
` 会返回当前 Shell 或脚本的进程号,可用于杀死当前进程(不常用,了解即可)。
示例验证:
# 查看当前 Shell 的 PID
[bq@shell ~]$ echo $$
2447 # 假设当前 Shell 的 PID 是 2447# 用 kill -9 强制终止当前进程(会断开连接)
[bq@shell ~]$ kill -9 $$
Connection closed by foreign host. # 进程被杀死,连接断开
3. $!
:获取后台进程的 PID 当我们用 &
让命令在后台运行时,$!
可以获取它的 PID,方便后续管理(如杀死后台进程)。
示例验证:
# 让 md5sum 在后台计算 /dev/zero(一个无限输出的设备)
[bq@shell ~]$ md5sum /dev/zero &
[1] 2611 # 后台进程的 PID 是 2611# 用 $! 获取后台进程的 PID
[bq@shell ~]$ echo $!
2611 # 输出刚才的 PID# 查看该 PID 对应的进程
[bq@shell ~]$ ps o pid,%cpu,%mem,command $!PID %CPU %MEM COMMAND2611 99.1 0.0 md5sum /dev/zero # 确实是后台运行的 md5sum# 用 $! 杀死后台进程
[bq@shell ~]$ kill $!
[1]+ Terminated md5sum /dev/zero # 进程被终止
4. $_
:获取上一个命令的最后一个参数 $_
会保存上一个命令中最后一个参数,方便快速复用。
示例验证:
# 执行一个包含多个参数的命令
[bq@shell ~]$ ls /etc/hosts /etc/fstab /etc/hostname
/etc/fstab /etc/hostname /etc/hosts# 用 $_ 获取最后一个参数(/etc/hostname)
[bq@shell ~]$ cat $_ # 等效于 cat /etc/hostname
bq-shell # 输出 /etc/hostname 的内容# 验证结果
[bq@shell ~]$ cat /etc/hostname
bq-shell # 和上面的输出一致
4.2 Shell 内置命令
Shell 自带了一些 “内置命令”(无法在目录中找到,由 Shell 直接提供),它们用于完成一些基础操作(如输出内容、读取输入、处理参数等)。下面介绍个人学习中常用的几个命令。
4.2.1 echo
:输出内容到终端*
echo
是最常用的内置命令,用于在终端打印文本,支持通过选项控制输出格式。
选项 | 作用说明 |
---|---|
-n | 输出内容后不自动换行 |
-e | 解析字符串中的转义字符(如下表) |
常用转义字符(需配合 -e
使用):
转义字符 | 作用说明 |
---|---|
\n | 换行 |
\t | 制表符(Tab 键) |
\b | 退格(删除前一个字符) |
示例验证:
# 1. -n 选项:不换行输出
[bq@shell ~]$ echo -n "bq "; echo "laowang" # 第一个 echo 不换行,第二个 echo 换行
bq laowang # 结果在同一行# 2. -e 解析 \n(换行)
[bq@shell ~]$ echo -e "bq\nlaowang" # \n 表示换行
bq
laowang # 分两行输出# 3. -e 解析 \t(制表符)
[bq@shell ~]$ echo -e "bq\tlaowang" # \t 表示 Tab 缩进
bq laowang # 中间有一个 Tab 空格# 4. -e 解析 \b(退格)
[bq@shell ~]$ echo -e "1\b23" # \b 删除前面的 "1"
23 # 结果只剩 "23"[bq@shell ~]$ echo -e "123\b" # \b 后面没有字符,无法删除
123 # 仍输出 "123"[bq@shell ~]$ echo -ne "123\b"; echo "haha" # -n 不换行,\b 删除 "3",再输出 "haha"
12haha # "123" 变成 "12",加上 "haha" 就是 "12haha"
4.2.2 read
:从终端读取输入
read
用于从用户输入中读取内容,并保存到变量中,常用于脚本中获取用户交互信息。
选项 | 作用说明 |
---|---|
-p | 显示提示信息(提示用户输入) |
-s | 输入内容不显示(适用于密码) |
示例验证:
# 1. 基本用法:读取输入并打印
[bq@shell ~]$ cat read.sh
#!/bin/sh
read -p "输入你想要说的话:" str # -p 显示提示,输入内容保存到 str 变量
echo "你想要说的话是:$str" # 打印变量内容# 执行脚本
[bq@shell ~]$ bash read.sh
输入你想要说的话:hello world # 输入内容
你想要说的话是:hello world # 输出变量值# 2. -s 选项:隐藏输入(适合密码)
[bq@shell ~]$ read -s -p "请设置用户密码: " password # -s 隐藏输入
请设置用户密码: # 输入时不显示内容
[bq@shell ~]$ echo $password # 验证输入的内容
redhat # 正确输出输入的密码
4.2.3 exec
:替换当前进程执行命令
exec
会用新的命令替换当前 Shell 进程(不创建子进程),新命令执行结束后,当前 Shell 也会终止。它也可以用于重定向输入输出。
示例 1:替换当前进程
# 切换到 root 用户,查看当前 Shell 的 PID
[root@shell ~]# ps $$ # $$ 是当前进程 PIDPID TTY STAT TIME COMMAND1741 pts/0 S 0:00 -bash # 当前 Shell 的 PID 是 1741# 用 exec 执行 sleep 10(替换当前进程)
[root@shell ~]# exec sleep 10 # 当前进程(1741)变成 sleep 进程# 在另一个窗口查看 PID 1741 的进程
[bq@shell ~]$ ps 1741PID TTY STAT TIME COMMAND1741 pts/0 S+ 0:00 sleep 10 # 确实替换成了 sleep# 10秒后 sleep 结束,当前 Shell 也终止,自动返回普通用户
[bq@shell ~]$
示例 2:重定向输入并读取文件 exec < 文件名
可以将脚本的标准输入重定向到文件,配合 read
可读取文件内容:
[bq@shell ~]$ cat exec.sh
#!/bin/bash# 生成一个包含 1-5 的文件
seq 5 > /tmp/seq.log # seq 5 输出 1-5,保存到 /tmp/seq.log# 将标准输入重定向到 /tmp/seq.log
exec < /tmp/seq.log # 后续 read 命令会从这个文件读取内容# 循环读取文件内容并打印
while read line # read 从标准输入(即文件)读取一行,保存到 line
doecho $line # 打印读取的内容
done# 执行脚本,输出文件内容
[bq@shell ~]$ bash exec.sh
1
2
3
4
5
4.3 Shell 变量子串:截取和处理变量内容
变量子串是对变量内容进行 “截取、替换、删除” 等操作的语法,类似于字符串处理函数,能帮我们快速处理变量中的内容。
4.3.1 常用变量子串语法
语法格式 | 作用说明 |
---|---|
${parameter} | 返回变量 parameter 的内容(最基础的变量引用) |
${#parameter} | 返回变量内容的长度(按字符计算,速度最快) |
${parameter/pattern/string} | 用 string 替换变量中第一个匹配 pattern 的子串 |
${parameter//pattern/string} | 用 string 替换变量中所有匹配 pattern 的子串 |
4.3.2 变量子串实践示例
实验准备:定义一个测试变量 str="abc123abc123"
(后续示例均基于此变量)。
[bq@shell ~]$ str="abc123abc123"
1. ${parameter}
:引用变量内容 最基础的用法,直接返回变量的值:
[bq@shell ~]$ echo ${str}
abc123abc123 # 输出变量 str 的内容
2. ${#parameter}
:获取变量长度 快速计算变量内容的字符个数(比其他方法更高效):
[bq@shell ~]$ echo ${#str}
12 # "abc123abc123" 共12个字符# 其他计算长度的方法(效率较低,了解即可)
[bq@shell ~]$ echo ${str} | wc -L # wc -L 统计最长行的长度
12
[bq@shell ~]$ expr length "${str}" # expr 工具计算长度
12
[bq@shell ~]$ echo "$str" | awk '{ print length($0)}' # awk 计算长度
12
3. 替换子串:${parameter/pattern/string}
和 ${parameter//pattern/string}
/
:只替换第一个匹配的子串//
:替换所有匹配的子串
# 只替换第一个 "abc" 为 "def"
[bq@shell ~]$ echo ${str/abc/def}
def123abc123 # 第一个 "abc" 被替换# 替换所有 "abc" 为 "def"
[bq@shell ~]$ echo ${str//abc/def}
def123def123 # 两个 "abc" 都被替换
4.3.3 实验应用案例
案例 1:批量修改文件名中的年份
# 创建一个测试文件
[bq@shell ~]$ touch stu-202212-snap.jpg
[bq@shell ~]$ ls stu-*
stu-202212-snap.jpg # 原文件名# 用变量子串替换年份(2022→2021)
[bq@shell ~]$ file="stu-202212-snap.jpg"
[bq@shell ~]$ mv $file ${file/2022/2021} # 替换第一个 2022 为 2021# 查看修改结果
[bq@shell ~]$ ls stu-*
stu-202112-snap.jpg # 年份已修改
案例 2:删除文件名中的特定字符串
# 创建一个测试文件
[bq@shell ~]$ touch stu-202212-snap.jpg
[bq@shell ~]$ ls stu-*
stu-202212-snap.jpg # 原文件名# 用变量子串删除 "-snap"
[bq@shell ~]$ file="stu-202212-snap.jpg"
[bq@shell ~]$ mv $file ${file/-snap/} # 用空字符串替换 "-snap"# 查看修改结果
[bq@shell ~]$ ls stu-*
stu-202212.jpg # "-snap" 已删除
如涉及版权问题,请联系作者处理!!!!