本文内容主要针对bash shell,不同shell之间可能存在差异。
一、shell扩展
Shell 在执行命令前,会对用户输入的内容进行一系列自动转换,这些转换操作统称为 Shell 扩展。主要作用是简化命令输入、增强脚本灵活性,并减少重复性操作。常见的扩展有大括号扩展、波浪号扩展、变量扩展、命令替换、文件名扩展。
Shell 会按照特定顺序依次尝试这些扩展。如果扩展有效(匹配到内容),则进行替换;否则,保持原样(当作普通字符串)不报错。这种机制需要注意潜在的非预期行为,例如未匹配的扩展可能导致参数传递错误。
1.1 防扩展措施
字符:使用Bash shell 中的转义字符——反斜杠( \ ) ,可以防止其后的单个字符被扩展。
字符串:使用单引号 (' ') 或双引号 (" ") 括起字符串。单引号可以阻止所有 shell 扩展,内容均按字面意义处理。双引号允许变量扩展( ${var} )和命令替换( $(cmd) ),但禁止其他扩展(如文件名扩展 、大括号扩展 {})。
1.2 文件名扩展
文件名扩展(也称为通配符扩展 )通过使用特殊字符(通配符)快速匹配文件和目录名,无需手动输入完整名称。当用户在命令参数中使用这些通配符时,Shell会自动将其展开为所有符合匹配规则的实际文件名列表。
模式匹配常用元字符
| 模式 | 匹配项 |
|---|---|
| * | 零个或更多字符组成的任意字符串 |
| ? | 任意一个字符 |
| [abc…] | 位于方括号之间的任意一个字符 |
| [!abc…] | 不在方括号之间的任意一个字符 |
| [^abc…] | 与上一行等价,!和 ^ 都表示不在 |
| [[:alpha:]] | 任意字母字符(不区分大小写) |
| [[:lower:]] | 任意小写字符 |
| [[:upper:]] | 任意大写字符 |
| [[:alnum:]] | 任意字母字符或数字 |
示例:
可以自己尝试一下每种情况,以及混搭的效果。以下只是简单示例:
ls ?????? # 文件名为6个字符的文件
![]()
ls [at]* # 以 a t 开头的文件
ls *.sh # 以 .sh 结尾的文件

![]()
ls [!a..mA..M]* # 列出当前目录中文件名不以字母 a/A 到 m/M 开头的所有文件

1.3 大括号扩展
大括号扩展用于生成字符串。
# 双点 (..)表示扩展为一个序列
# echo会将内容输出到终端
echo {1..5} # 等价于 echo {1,2,3,4,5}
echo {a..c} # 等价于 echo {a,b,c}

支持组合使用,比如 {A,B,C}{1,2} 会生成"A1 A2 B1 B2 C1 C2",便于创建目录或批量操作文件。以下是一个示例:
目前有五部电视剧,分别叫做Good_TV_Drama1,Good_TV_Drama2,Good_TV_Drama3,Good_TV_Drama4,Good_TV_Drama5,每部电视剧都有一个目录用于存放剧集,每部1-15集。
不使用大括号扩展的如下:
mkdir Good_TV_Drama1
cd Good_TV_Drama1
touch Episode01, Episode02, Episode03, Episode04, Episode05, Episode06, Episode07, Episode08, Episode09, Episode10, Episode11, Episode12, Episode13, Episode14, Episode15
cd ..
# 2,3,4重复上述操作
# 当然,这种重复性的操作可以采用循环(脚本编写部分介绍)
for i in 1 2 3 4 5; do
mkdir "Good_TV_Drama$i"
cd "Good_TV_Drama$i" || exit
touch Episode{01..15}
cd ..
done
以下是采用大括号扩展,工作量骤减:
mkdir Good_TV_Drama{1..5}
touch Good_TV_Drama{1..5}/Episode{01..15}
# 也可以合并到一行
mkdir Good_TV_Drama{1..5} && touch Good_TV_Drama{1..5}/Episode{01..15}

1.4 波浪号扩展
波浪号~可扩展为当前用户的家目录,就像手机的home键。
~ # 当前用户家目录
~user # 指定用户的家目录
常见用法如下:
# 用法不止于此,涉及需要对家目录(/home/用户名)的操作,都可以使用 ~ 代替
cd ~ # 回到家目录
cd ~/dir # 进入家目录下的dir目录中
ls -l ~/ # 查看家目录下的信息
ls ~username # 查看指定用户的家目录(前提是要有权限)
1.5 变量扩展
变量扩展可以将变量名替换为存储的值。当 Shell 遇到以美元符号($)开头的字符串时,会将其识别为变量引用,并尝试查找对应的变量值进行替换。例如,$HOME 会被扩展为当前用户的家目录路径,$PATH 会被拓展为环境变量。
# 查看当前环境变量的值
echo ${PATH} # ${PATH}等价于波浪号~拓展

为了确保变量扩展的准确性,建议始终用花括号({})包裹变量名。这种方式能明确界定变量名的边界,避免歧义。
Shell 变量命名遵循以下规则:
- 允许的字符包括字母(区分大小写)、数字和下划线(
_)。 - 变量名不能以数字开头,如 6var 是无效的。
- 变量名区分大小写,因此 $PATH 和 $Path 是两个不同的变量。
这部分内容可以查看这篇文章:
How To Use Bash Parameter Substitution Like A Pro - nixCraft
https://www.cyberciti.biz/tips/bash-shell-parameter-substitution-2.html
1.5.1 变量定义
# 定义变量(等号两边不能有空格)
variable_name="value"
# 定义数字变量
count=10
# 定义数组
array=("apple" "banana" "cherry")
1.5.2 变量使用
${var} # var替换为具体变量名
# 以下了解,需要时可查看上面的文章
${var#prefix} # 删除最短匹配前缀
${var##prefix} # 删除最长匹配前缀
${var%suffix} # 删除最短匹配后缀
${var%%suffix} # 删除最长匹配后缀
${var/old/new} # 替换第一个匹配
${var//old/new} # 替换所有匹配
${var:start:len} # 子字符串提取
后期如果有遇到常用的,会进行补充。
1.6 命令替换
命令替换将一个命令的输出结果直接嵌入到另一个命令行中使用。当使用 $(command) 语法时,Shell 会先执行括号内的命令,然后将其标准输出内容替换到原命令位置,其支持多级嵌套,比如 $(command1$(command2)) 。
$(command) # command替换为具体命令
`command` # 较旧的格式,使用反引号,不推荐,容易和单引号(') 弄混
注:反引号 ( ` )是英文模式下键盘左上角 Esc 键下方的按键。
# 将日期嵌入到文件名中
backup_file="backup-$(date +%Y%m%d).tar.gz"
echo "备份文件名: $backup_file"

补充:变量扩展和命令替换都是以美元符号($)开头的,系统如何区分呢?如果同名呢?
答:变量扩展优先级高于命令替换,在同名且未使用 () 或者 {} 时,系统优先匹配变量,未找到的情况下匹配命令替换。使用时最好带上 () 或者 {}。
二、shell脚本编写
Shell脚本是用Shell命令编写的可执行文件,本质上是一系列Linux/Unix命令的集合,包含Shell命令和逻辑控制语句,能够自动化执行系统任务,告诉系统按特定顺序执行各种命令,适合完成系统管理、文件处理和日常自动化工作。
特点:
- 直接调用系统原生命令,系统管理效率高
- 无需编译即可运行
局限性:
- 不适合复杂计算或大型应用
- 性能不如编译型语言
- 不同Shell版本兼容性问题
2.1 指定解释器
shell脚本的第一行#!称为she-bang(也称hash-bang),由 #(hash/sharp)和 !(bang)组成,用于指定脚本的解释器路径。当运行脚本时,系统内核会读取这行指令,自动调用对应的解释器来执行脚本内容。
# 使用Bash执行该脚本
#!/usr/bin/bash # 直接指定系统的 bash 路径
#!/usr/bin/env bash # 通过 env 查找 bash(更好的可移植性)
2.2 脚本代码结构
以下内容和其他语言(c/c++等)的结构很类似,所以介绍的较为简略,可以在后续章节的具体示例中查看用法。
2.2.1 if条件判断
# 以下的空格不能省略,不能是[[条件]]
if [[ 条件 ]]; then
# 条件成立时执行的代码
elif [[ 条件 ]]; then
# 其他条件成立时执行的代码
else
# 所有条件均不成立时执行的代码
fi
注:[ ]或 [[ ]] 用于条件测试,操作符和操作数之间必须有空格。推荐 [[ ]] ,其支持字符串模式匹配、逻辑运算符组合、正则表达式等。
| 结构 | 用途 | 支持的数据类型 |
|---|---|---|
|
| 算术运算 | 整数(直接支持) |
|
| 条件测试 | 字符串/文件/布尔逻辑 |
2.2.2 条件判断运算符
数值比较操作符
-
-eq(equal) - 等于 -
-ne(not equal) - 不等于 -
-lt(less than) - 小于 -
-gt(greater than) - 大于 -
-le(less or equal) - 小于等于 -
-ge(great or equal) - 大于等于
字符串比较操作符
-
==或=- 字符串相等 -
!=- 字符串不等 -
-z- 字符串长度为零(空字符串) -
-n- 字符串长度非零(非空字符串)
文件测试操作符
-
-e- 文件/目录是否存在 -
-f- 是普通文件 -
-d- 是目录 -
-L- 是符号链接 -
-r- 可读 -
-w- 可写 -
-x- 可执行 -
-s- 文件大小大于零(非空)
2.2.3 for循环
for 变量 in 列表; do
# 循环体
done
2.2.4 while循环
while [[ 条件 ]]; do
# 循环体(条件成立时执行)
done
2.2.5 case分支选择
case 变量 in
情况1)
# 情况1对应代码
;;
情况2)
# 情况2对应代码
;;
*)
# 默认情况(可选)
;;
esac
2.3 执行 bash 脚本
1. 执行之前需要先确定脚本的所在路径:
1)若脚本位于shell的PATH环境变量列出的目录中,则可以直接使用文件名运行shell脚本。需要注意的是PATH 解析会运行匹配的第一个文件名,因此不要使用现有的命令名称来命名脚本文件。
使用 which 命令可以查询文件所在路径:
注意:which 命令只能查找 PATH 环境变量所列目录中的可执行文件,别名(比如,ll )和Shell 内建命令(比如,cd )或者不在PATH中的无法找到。
which [文件名]
which tree # tree是一个显示目录结构的工具
![]()
2)如果脚本不在 PATH 目录中,则需要使用绝对路径或者相对路径名称运行。
2. 具体的执行方式有两种,一种是作为用户执行,另一种是使用bash命令执行。
1)作为用户执行时,无论是否在环境变量中都需要有脚本的执行(x)和读取(r)权限。(权限相关的文章正在完善中,后期会在此处放链接)

通过ls命令可以看出缺少执行(x)权限,脚本无法运行。添加之后脚本可正常运行。
chmod a+x test_auth.sh
./test_auth.sh # 脚本需在当前目录下

2)使用bash命令执行时会强制使用bash解释器执行,忽略脚本自身的shebang,所以不需要脚本具有执行权限(但仍需读取权限)。

bash [脚本名]
bash test_auth.sh

2.4 bash 脚本退出代码
Shell脚本的退出代码机制与其他编程语言(如C/C++)中的函数返回值的概念类似。以c/c++为例,在函数中常通过返回值判断函数的执行情况。这里的脚本退出代码也是同理,用来判断脚本的执行情况。正常情况下为0,表示成功,非0表示失败,具体非0值的含义由脚本具体内容决定,通常用于区分不同类型的错误。
比如以下代码,在找不到日志文件时,返回1。

使用 $? 特殊变量可以获取上一个命令/脚本的退出状态。
echo $?

预告:本系列下一篇文章为grep、sed、awk三剑客详解
如有问题或建议,欢迎在评论区中补充~
333

被折叠的 条评论
为什么被折叠?



