一,shell介绍
Shell是一个命令行解释器,它为用户提供了一个向Linux内核发送请求以便运行程序的界面系统级程序。
用户可以用Shell来启动,挂起,停止,编写一些程序。

二,Shell脚本执行方式
2.1 格式要求
- 脚本以
#!/bin/bash
开头 - 脚本需要有可执行权限
- 一般shell脚本文件以
.sh
为后缀 - 在shell中用
#
表示注释 - 多行注释为 起始处
:<<!
结尾处!
创建的shell脚本文件即使是root权限也不会给x执行权限,需要自己增加
2.2 常用执行方式
运行方式:
- 直接执行(
./script.sh
) - 通过解释器运行(
sh script.sh
)。
三,变量
在 Shell 脚本中,变量是存储数据的核心工具,可分为 环境变量 、局部变量 、特殊变量 、数组变量 等类型。
3.1 变量分类与作用域
类型 | 作用域 | 生命周期 | 定义方式 |
---|---|---|---|
局部变量 | 当前 Shell 进程 | 脚本或函数执行期间 | var=value |
环境变量 | 当前及子进程 | 直到 Shell 会话结束 | export var=value 或 var=value; export var |
特殊变量 | 全局 | 脚本执行期间 | 预定义(如 $0 , $# , $? 等) |
只读变量 | 不可修改 | 定义后不可更改 | readonly var=value |
数组变量 | 当前 Shell 进程 | 脚本或函数执行期间 | array=(val1 val2) |
3.2 变量的定义与使用
基本语法
-
赋值 :
变量名=值
(等号两侧不能有空格) -
引用 :
$变量名
或${变量名}
(推荐后者,明确变量边界) -
删除 :
unset 变量名
-
示例
bashname="Alice" # 定义局部变量 echo "Hello, $name" # 输出:Hello, Alice echo "Hello, ${name}" # 更安全的写法 unset name # 删除变量
环境变量
-
作用:传递到子进程(如脚本调用的其他程序)。
-
定义方式:
bashexport PATH="/usr/bin:$PATH" # 直接导出 # 或先赋值再导出 JAVA_HOME="/opt/java" export JAVA_HOME
只读变量
-
特性:定义后不可修改或删除。
bashreadonly PI=3.14 PI=3.1415 # 报错:PI: readonly variable
特殊变量
-
Shell 预定义的特殊变量,用于获取脚本运行时的上下文信息:
变量 含义 $0
当前脚本的文件名 $1
,$2
, ...脚本的参数( $1
是第一个参数,以此类推),但是如果是10就需要${10}$#
传递给脚本的参数个数 $@
所有参数列表(每个参数独立,如 "$@"
会保留参数中的空格)$*
所有参数合并为一个字符串(如 "$*"
相当于"$1 $2 ..."
)$?
上一条命令的退出状态(0 表示成功,非 0 表示失败) $$
当前 Shell 进程的 PID $!
最后一个后台运行进程的 PID -
示例
bash#!/bin/bash echo "脚本名:$0" # 输出脚本文件名 echo "第一个参数:$1" # 输出传入的第一个参数 echo "参数个数:$#" # 输出参数总数 echo "所有参数:$@" # 输出参数列表(推荐使用) echo "上一条命令状态:$?" # 检查命令是否成功
数组变量
-
定义与访问
bash# 定义数组 fruits=("apple" "banana" "cherry") # 索引数组 declare -A user=([name]="Alice" [age]=30) # 一个Map数组 # 访问元素 echo ${fruits[0]} # 输出:apple(索引从0开始) echo ${user["name"]} # 输出:Alice #获取所有元素 echo ${fruits[@]} # 输出所有元素 echo ${!user[@]} # 输出所有键(关联数组)
-
修改数组
bashfruits[1]="blueberry" # 修改第二个元素 fruits+=("orange") # 追加元素 unset fruits[2] # 删除第三个元素
3.3 变量替换与字符串操作
变量替换:
语法 | 说明 |
---|---|
${var:-default} |
若 var 未定义,返回 default |
${var:=default} |
若 var 未定义,赋值为 default 并返回 |
${var:+replacement} |
若 var 已定义,返回 replacement |
${var:?error_msg} |
若 var 未定义,输出错误信息并终止脚本 |
示例:
bash
echo ${name:-"Guest"} # 若name未定义,输出Guest
字符串操作
语法 | 说明 |
---|---|
${#string} |
获取字符串长度 |
${string:start:length} |
截取子字符串(从 start 开始,长度 length ) |
${string#pattern} |
删除最短匹配前缀 |
${string##pattern} |
删除最长匹配前缀 |
${string%pattern} |
删除最短匹配后缀 |
${string%%pattern} |
删除最长匹配后缀 |
${string/old/new} |
替换第一个匹配的 old 为 new |
${string//old/new} |
替换所有匹配的 old 为 new |
示例:
bash
path="/home/user/file.txt"
echo ${path#*/} # 输出:home/user/file.txt(删除第一个/及之前内容)
echo ${path##*/} # 输出:file.txt(删除最后一个/及之前内容)
echo ${path%.*} # 输出:/home/user/file(删除最后一个.及之后内容)
echo ${path/file/doc} # 输出:/home/user/doc.txt
3.4 变量的引号使用
引号类型 | 作用 |
---|---|
无引号 | 变量值中的空格会触发单词分割,通配符(* )会被扩展 |
双引号 | 保留变量值中的空格和特殊字符($ , \ , ! 等),变量会被替换 |
单引号 | 完全原样输出,变量和特殊字符不会被替换 |
示例:
bash
name="Alice Bob"
echo $name # 输出:Alice Bob(正确,但若包含特殊字符可能出错)
echo "$name" # 推荐写法,避免空格分割
echo '$name' # 输出:$name(变量不会被替换)
3.5 注意事项
- 命名规则 :
- 变量名由字母、数字、下划线组成,不能以数字开头。
- 避免使用 Shell 关键字(如
if
,then
)。
- 空格问题 :
- 赋值时等号两侧不能有空格:
var=value
(正确),var = value
(错误)。
- 赋值时等号两侧不能有空格:
- 引号必要性 :
- 处理含空格的字符串时,必须用双引号包裹变量:
"$var"
。
- 处理含空格的字符串时,必须用双引号包裹变量:
- 作用域陷阱 :
- 函数内未使用
local
定义的变量会污染全局作用域。
- 函数内未使用
四,运算符
4.1 算术运算符
用于数值计算(需用 $(( ))
或 expr
)。
运算符 | 说明 | 示例 |
---|---|---|
+ |
加法 | echo $((5 + 3)) → 输出 8 |
- |
减法 | echo $((10 - 2)) → 输出 8 |
* |
乘法 | echo $((4 * 2)) → 输出 8 |
/ |
除法 | echo $((16 / 2)) → 输出 8 |
% |
取模(取余) | echo $((9 % 2)) → 输出 1 |
** |
幂运算 | echo $((2 ** 3)) → 输出 8 |
示例脚本:
bash
a=10
b=3
sum=$((a + b)) # sum=$(expr $a + $b) ,不建议用expr因为expr中*需要用\*进行转义,而且变量间需要空格
echo "和为:$sum" # 输出:和为:13
4.2 比较运算符
用于数值或字符串的条件判断(需在 [ ]
或 [[ ]]
中使用)。
运算符 | 说明 | 示例 |
---|---|---|
-eq |
等于 | [ $a -eq $b ] |
-ne |
不等于 | [ $a -ne $b ] |
-gt |
大于 | [ $a -gt $b ] |
-lt |
小于 | [ $a -lt $b ] |
-ge |
大于等于 | [ $a -ge $b ] |
-le |
小于等于 | [ $a -le $b ] |
示例脚本:
bash
if [ $a -gt 5 ]; then
echo "a 大于 5"
fi
字符串比较
运算符 | 说明 | 示例 |
---|---|---|
= |
等于 | [ "$str1" = "$str2" ] |
!= |
不等于 | [ "$str1" != "$str2" ] |
-z |
空字符串 | [ -z "$str" ] |
-n |
非空字符串 | [ -n "$str" ] |
示例脚本:
bash
str1="hello"
str2="world"
if [ "$str1" != "$str2" ]; then
echo "字符串不同"
fi
4.3 逻辑运算符
用于组合多个条件。
运算符 | 说明 | 示例 | ||
---|---|---|---|---|
&& |
逻辑与 | [ 条件1 ] && [ 条件2 ] |
||
` | ` | 逻辑或 | `[ 条件1 ] | [ 条件2 ]` |
! |
逻辑非 | [ ! 条件 ] |
示例:
bash
if [ $a -gt 5 ] && [ $a -lt 15 ]; then
echo "a 在 5 和 15 之间"
fi
4.4 字符串操作符
专用于字符串操作。
运算符 | 说明 | 示例 |
---|---|---|
${#s} |
获取字符串长度 | echo ${#str} → 输出长度 |
${s:pos} |
截取子字符串(从位置 pos 开始) | echo ${str:2} → 从第3字符开始 |
${s:pos:len} |
截取子字符串(从 pos 开始,长度 len) | echo ${str:1:3} → 截取3字符 |
示例:
bash
str="abcdef"
echo "${str:2}" # 输出:cdef
echo "${str:1:3}" # 输出:bcd
4.5 文件测试运算符
用于检查文件/目录属性。
运算符 | 说明 | 示例 |
---|---|---|
-e |
文件/目录是否存在 | [ -e "/path/file" ] |
-f |
是否为普通文件 | [ -f "/path/file" ] |
-d |
是否为目录 | [ -d "/path/dir" ] |
-r |
是否可读 | [ -r "/path/file" ] |
-w |
是否可写 | [ -w "/path/file" ] |
-x |
是否可执行 | [ -x "/path/file" ] |
-s |
文件大小是否大于0字节 | [ -s "/path/file" ] |
示例:
bash
file="/tmp/test.txt"
if [ -f "$file" ]; then
echo "$file 是普通文件"
fi
4.6 赋值运算符
用于变量赋值和运算简化。
运算符 | 说明 | 示例(假设初始值 a=5 ) |
---|---|---|
= |
赋值 | a=10 → a 变为 10 |
+= |
追加字符串 | str="hi"; str+=" there" → "hi there" |
+= |
数值累加 | ((a += 3)) → a 变为 8 |
示例:
bash
num=5
((num *= 2)) # num 变为 10
echo $num
五,条件判断
5.1 单分支
bash
if [ 判断条件 ]
then
条件为true执行的语句
fi
5.2 双分支
shell
if [ 判断条件 ]
then
条件为true执行的语句
else
条件为flase执行的语句
fi
简写:
shell
[ 判断条件 ] && 条件为true执行语句
注意点:
- 判断条件前后要有空格
- if 和条件之间需要空格
[ ]
返回false(有空格!)[ wwadwa ]
返回true0
为true ,>1
为false
5.3 多分支
bash
if [ 条件判断 ]
then
代码
elif [ 条件判断 ]
then
代码
...
fi
5.4 流程控制(case)
基本语法:
bash
case $变量名 in
"值1")
代码1
;;
"值2")
代码2
;;
.....
*)
最终情况
;;
esac
5.5 常用判断条件
在前面加上 !
即为否定,比如 ! -f
文件不存在
字符串之间比较
shell
if [ 字符串1 = 字符串2 ]
两个整数的比较
shell
if [ 1 -lt 2 ]
符号 | 描述 |
---|---|
-lt | 小于 |
-le | 小于等于 |
-eq | 等于 |
-gt | 大于 |
-ge | 大于等于 |
-ne | 不等于 |
按照文件权限判断
shell
if [ 选项 文件路径 ]
符号 | 描述 |
---|---|
-r | 有读的权限 |
-w | 有写的权限 |
-x | 有执行的权限 |
按照文件类型进行判断
shell
if [ 符号 文件路径 ]
符号 | 描述 |
---|---|
-f | 存在文件 |
-e | 存在路径 |
-d | 存在目录 |
示例:
bash
if [ $1 -gt 100 ];
then
echo "参数大于100"
elif [ -f "/path/to/file" ];
then
echo "文件存在"
else
echo "其他情况"
fi
六,循环
6.1 for循环
基本语法:遍历列表或范围
bash
for 变量 in 列表;
do
命令
done
典型应用
-
遍历数字范围:
bashfor i in {1..5}; do echo "数字:$i" done # 输出:数字1到5
-
遍历文件/目录:
bashfor file in *.txt; do echo "处理文件:$file" done
-
遍历数组:
bashfruits=("apple" "banana" "cherry") for fruit in "${fruits[@]}"; do echo "水果:$fruit" done
-
C 风格循环(仅限 Bash):
bashfor ((i=0; i<5; i++)); do echo "计数器:$i" done
6.2 while循环
基本语法:
bash
while [ 条件 ];
do
命令
done
典型应用
-
读取文件内容:
bashwhile read line; do echo "行内容:$line" done < file.txt
-
计数器控制:
bashcount=0 while [ $count -lt 5 ]; do echo "计数:$count" ((count++)) done
-
无限循环:
bashwhile true; do echo "按 Ctrl+C 终止" sleep 1 done
6.3 until
循环
基本语法 :条件为假时持续执行(与 while
逻辑相反):
bash
until [ 条件 ];
do
命令
done
典型应用
-
等待条件满足:
bashuntil ping -c1 example.com &>/dev/null; do echo "等待网络连接..." sleep 1 done echo "连接成功!"
&>/dev/null
- 作用 :将命令的 所有输出(包括标准输出和错误输出)重定向到黑洞 (即不显示任何信息)。
>
:重定向。/dev/null
:Linux 中的虚拟设备,丢弃所有写入的数据。&>
:同时重定向标准输出(stdout)和错误输出(stderr)。
- 效果:让命令静默执行,不在终端显示结果。
- 作用 :将命令的 所有输出(包括标准输出和错误输出)重定向到黑洞 (即不显示任何信息)。
6.4 循环控制语句
关键字 | 作用 | 示例 |
---|---|---|
break |
立即退出循环 | if [条件]; then break; fi |
continue |
跳过当前迭代,进入下一次循环 | if [条件]; then continue; fi |
exit |
终止整个脚本 | if [错误]; then exit 1; fi |
示例:
bash
for i in {1..10};
do
if [ $i -eq 5 ];
then
break # 当i=5时退出循环
fi
echo "当前值:$i"
done
# 输出:1 2 3 4
七,读取控制台输入内容
基本语法
bash
read (选项) (参数) 接受值得变量
选项
-p
指定读取值时的提示信息-t
指定读取值的等待时间,如果未在规定时间输入就不等待
举例
shell
read -p "请输入一个值num=" NUM
其中NUM1是接受从控制台输入值的变量
-p
后面的内容是从控制台等待读取值的时候的提示信息
这个相当于scanf("请输入一个值num=%d",$NUM1)
shell
read -t 10 NUM2
等待10s如果10后没有获取到数值就停止等待
结合起来写法
shell
read -p 参数 -t 参数 接受值的变量
八,函数
在 Shell 脚本中,函数(Function)是将一组命令封装为可重复调用的代码块,用于提高代码复用性和逻辑模块化。以下是 Shell 函数的完整指南:
8.1 函数定义与调用
定义语法
bash
# 方式1:标准定义
function_name() {
命令
}
# 方式2(仅限 Bash):使用 `function` 关键字
function function_name {
命令
}
调用函数
直接通过函数名调用,无需括号:
bash
function_name
示例
bash
# 定义函数
say_hello() {
echo "Hello, $1!"
}
# 调用函数并传参
say_hello "Alice" # 输出:Hello, Alice!
8.2 函数参数
传递参数
函数内通过 $1
, $2
, ..., $n
接收参数:
bash
sum() {
result=$(( $1 + $2 ))
echo "和为:$result"
}
sum 5 3 # 输出:和为:8
特殊参数
变量 | 含义 |
---|---|
$# |
函数接收的参数个数 |
$@ |
所有参数的列表(数组) |
$* |
所有参数合并为字符串 |
示例:
bash
show_args() {
echo "参数个数:$#"
echo "参数列表:$@"
}
show_args "A" "B" "C"
# 输出:
# 参数个数:3
# 参数列表:A B C
8.3 返回值与退出状态
return
语句
- 函数默认返回最后一条命令的退出状态码(0 表示成功,非 0 表示失败)。
- 使用
return
显式返回状态码(范围:0~255)。
bash
check_file() {
if [ -f "$1" ]; then
return 0 # 文件存在 → 成功
else
return 1 # 文件不存在 → 失败
fi
}
check_file "test.txt"
echo $? # 输出返回值(0 或 1)
返回数据
- 通过变量传递:使用全局变量存储结果。
- 通过
echo
输出 :结合命令替换($(...)
)捕获输出。
bash
# 方式1:全局变量
result=""
calculate() {
result=$(( $1 * 2 ))
}
calculate 5
echo $result # 输出:10
# 方式2:echo 输出
double() {
echo $(( $1 * 2 ))
}
value=$(double 5)
echo $value # 输出:10
8.4 变量作用域
- 默认全局变量:函数内外定义的变量均为全局变量。
- 局部变量 :使用
local
关键字定义仅在函数内有效的变量。
bash
demo_scope() {
local local_var="内部变量"
global_var="全局变量"
}
demo_scope
echo $global_var # 输出:全局变量
echo $local_var # 输出:空(变量不存在)
8.5 递归函数
函数可以调用自身,但需注意终止条件以避免无限递归。
bash
factorial() {
if [ $1 -le 1 ]; then
echo 1
else
local prev=$(factorial $(( $1 - 1 )))
echo $(( $1 * prev ))
fi
}
echo "5的阶乘:$(factorial 5)" # 输出:120
8.6 函数库与导入
将常用函数封装为独立文件(如 utils.sh
),通过 source
导入使用:
bash
# utils.sh
log() {
echo "[$(date)] $1"
}
# main.sh
source utils.sh
log "任务开始" # 输出带时间戳的日志
8.7 实用技巧
函数别名
使用 alias
为常用函数创建快捷命令:
bash
# 定义函数
docker_clean() {
docker system prune -af
}
# 设置别名(可选)
alias dclean="docker_clean"
调试函数
-
显示执行过程:
bashset -x # 开启调试 my_function set +x # 关闭调试
-
逐行调试:
bashbash -x script.sh
错误处理
使用 trap
捕获错误信号:
bash
handle_error() {
echo "错误发生在第 $LINENO 行"
exit 1
}
trap 'handle_error' ERR
8.8 实际应用示例
文件备份函数
bash
backup_file() {
local src="$1"
local dest="${src}.bak_$(date +%Y%m%d)"
if cp "$src" "$dest"; then
echo "备份成功:$dest"
else
echo "备份失败!" >&2
return 1
fi
}
backup_file "data.txt"
用户输入验证
bash
get_input() {
read -p "请输入用户名(长度≥3):" username
if [ ${#username} -lt 3 ]; then
echo "用户名太短!" >&2
return 1
fi
echo "$username"
}
valid_name=$(get_input) || exit 1
8.9 注意事项
- 避免函数名冲突:函数名不要与系统命令重复。
- 参数校验 :在函数内检查参数合法性(如
if [ $# -lt 1 ]; then ...
)。 - 性能优化:避免在循环中频繁调用复杂函数。