Shell 脚本基础
Shell 是 Linux 的命令解释器,也是强大的脚本语言。Bash (Bourne Again Shell) 是最常用的 Shell。
Shell 基础
Set
set -x会开启 执行跟踪模式(execution tracing)。
👉 Bash 会在执行每一条命令前,把“展开后的命令”打印出来(通常输出到 stderr)。
常用于:
- 调试脚本
- 查看变量展开结果
- 排查逻辑问题
bash
#!/bin/bash
set -x
echo "Start"
name="Phil"
echo "Hello $name"
ls /tmp1
2
3
4
5
6
7
2
3
4
5
6
7
运行:
text
+ echo Start
Start
+ name=Phil
+ echo 'Hello Phil'
Hello Phil
+ ls /tmp
file1
file21
2
3
4
5
6
7
8
2
3
4
5
6
7
8
| 选项 | 作用 |
|---|---|
| -x | 打印执行过程 |
| -e | 遇到错误立即退出 |
| -u | 使用未定义变量时报错 |
| -o pipefail | 管道失败时返回错误 |
bash
#!/bin/bash # Shebang - 指定解释器
# Author: Your Name
# Description: Script description
set -e # 遇到错误时退出
set -u # 使用未定义变量时报错
set -o pipefail # 管道命令失败时退出
# 脚本主体
echo "Hello, World!"1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
注释
bash
# 单行注释
: <<'COMMENT'
多行注释
可以写多行内容
COMMENT1
2
3
4
5
6
2
3
4
5
6
引号
| 引号 | 变量展开 $var | 转义字符 \n | 用途 |
|---|---|---|---|
' ' | ❌ | ❌ | 纯文本,原样输出 |
" " | ✅ | ❌ | 日常使用 |
$' ' | ✅ | ✅ | 含换行、制表符 |
bash
# 单引号:完全字面量
echo '$HOME \n' # 输出: $HOME \n
# 双引号:变量展开,但转义不生效
echo "$HOME \n" # 输出: /home/user \n
# ANSI-C 引号:支持 \n、\t 等转义
echo $'hello\nworld' # 输出换行1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
数组
普通数组
bash
# 定义数组
files=("file1.txt" "file2.txt" "file3.txt")
# 访问数组
echo ${files[0]} # 第一个元素
echo ${files[@]} # 所有元素
echo ${#files[@]} # 数组长度
# 遍历数组
for file in "${files[@]}"; do
echo "$file"
done
# 添加元素
files+=("file4.txt")
# 删除元素
unset files[1]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
关联数组
bash
# 声明关联数组
declare -A config
# 赋值
config[host]="localhost"
config[port]="3306"
# 访问
echo ${config[host]}
echo ${config[@]}
# 遍历
for key in "${!config[@]}"; do
echo "$key: ${config[$key]}"
done1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
函数
if 语句
bash
# 单分支
if [ condition ]; then
echo "Condition is true"
fi
# 双分支
if [ condition ]; then
echo "True"
else
echo "False"
fi
# 多分支
if [ condition1 ]; then
echo "Case 1"
elif [ condition2 ]; then
echo "Case 2"
else
echo "Default"
fi1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
条件 - 条件测试
bash
# 文件测试
[ -f file.txt ] # 文件存在且为普通文件
[ -d directory ] # 目录存在
[ -e path ] # 路径存在
[ -r file.txt ] # 文件可读
[ -w file.txt ] # 文件可写
[ -x file.txt ] # 文件可执行
[ -s file.txt ] # 文件非空
# 字符串比较
[ "$str1" = "$str2" ] # 字符串相等
[ "$str1" != "$str2" ] # 字符串不等
[ -z "$str" ] # 字符串为空
[ -n "$str" ] # 字符串非空
# 数值比较
[ $num1 -eq $num2 ] # 等于
[ $num1 -ne $num2 ] # 不等于
[ $num1 -gt $num2 ] # 大于
[ $num1 -ge $num2 ] # 大于等于
[ $num1 -lt $num2 ] # 小于
[ $num1 -le $num2 ] # 小于等于
# 逻辑运算
[ -a ] # 逻辑与(AND)
[ -o ] # 逻辑或(OR)
! # 逻辑非(NOT)
# 使用 [[ ]] 更强大(支持 &&、||、正则)
if [[ $name =~ ^J ]]; then
echo "Name starts with J"
fi1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
循环 - for
bash
# 遍历列表
for item in item1 item2 item3; do
echo "$item"
done
# 遍历范围
for i in {1..10}; do
echo "Number: $i"
done
# C 风格
for ((i=0; i<10; i++)); do
echo "$i"
done
# 遍历文件
for file in *.txt; do
echo "Processing: $file"
done1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
循环 - while
bash
# 基本用法
count=0
while [ $count -lt 5 ]; do
echo "Count: $count"
((count++))
done
# 读取文件
while IFS= read -r line; do
echo "$line"
done < file.txt1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
自定义函数
bash
# 定义函数
function_name() {
local var="local" # 局部变量
echo "Function: $1"
return 0 # 返回值(0-255)
}
# 调用函数
function_name "argument"
# 返回值
check_file() {
if [ -f "$1" ]; then
return 0 # 成功
else
return 1 # 失败
fi
}
if check_file "file.txt"; then
echo "File exists"
fi1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Argument
特殊变量
bash
# 脚本参数
$0 # 脚本名称
$1 - $9 # 第 1 到第 9 个参数
$@ # 所有参数(每个参数独立)
$* # 所有参数(作为整体)
$# # 参数个数
# 返回状态
$? # 上一条命令的退出状态(0=成功)
$$ # 当前脚本的 PID
$! # 最近后台进程的 PID
# 命令历史
!! # 上一条命令
!$ # 上一条命令的最后一个参数1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
通配符
bash
* # 匹配任意字符(0个或多个)
? # 匹配单个字符
[abc] # 匹配括号内任一字符
[a-z] # 匹配小写字母
[^abc] # 匹配非 abc 的字符
{a,b,c} # 匹配 a 或 b 或 c
{1..5} # 匹配 1 到 51
2
3
4
5
6
7
2
3
4
5
6
7
花括号扩展
bash
# 创建多个文件
touch file{1..5}.txt # file1.txt 到 file5.txt
touch {a,b,c}.sh # a.sh, b.sh, c.sh
# 批量操作
cp image.{png,jpg} /backup/ # 复制 png 和 jpg
mkdir -p {src,build,test}/utils # 创建多级目录1
2
3
4
5
6
7
2
3
4
5
6
7
实用技巧
bash
# 备份文件
cp file.txt{,.bak} # 快速备份
# 重命名
mv old.{txt,md} # old.txt -> old.md1
2
3
4
5
2
3
4
5
Stream
管道
bash
# 标准管道
command1 | command2
# 示例
ps aux | grep nginx
cat file.txt | grep pattern
ls -l | sort -k51
2
3
4
5
6
7
2
3
4
5
6
7
tee - 分支输出
bash
# 同时输出到文件和屏幕
command | tee output.txt
# 追加模式
command | tee -a output.txt1
2
3
4
5
2
3
4
5
重定向
bash
# 标准输出重定向
command > file.txt # 覆盖
command >> file.txt # 追加
# 标准错误重定向
command 2> error.txt
command 2>> error.txt
# 同时重定向
command &> output.txt # 标准输出和错误
command > output.txt 2>&1 # 等价写法
# 丢弃输出
command > /dev/null 2>&11
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
Environment Variable
变量定义与使用
bash
# 定义变量(等号两边不能有空格)
name="John"
age=25
# 使用变量
echo $name
echo ${name} # 推荐使用花括号
# 只读变量
readonly PI=3.14
# 删除变量
unset name1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
变量替换
bash
# ${变量:-默认值} - 变量未设置或为空时使用默认值
echo ${name:-"Guest"}
# ${变量:=默认值} - 变量未设置或为空时设置并使用默认值
echo ${count:=0}
# ${变量:+替换值} - 变量设置时使用替换值
echo ${name:+"Hello"}
# ${#变量} - 获取变量长度
echo ${#name}
# 字符串操作
echo ${name:0:2} # 截取子串(从位置 0 开始,长度 2)
echo ${name/j/J} # 替换第一个匹配
echo ${name//j/J} # 替换所有匹配1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
命令替换
Command Substitution(命令替换)
把一个命令的输出,当成字符串插入到另一个命令中。
语法:
bash
$(command)1
旧语法(不推荐):
bash
`command`1
例 1
bash
echo "Today is $(date)"1
会变成:
bash
echo "Today is Tue Feb 14 ..."1
例 2: 把文件复制为带今天日期的备份文件
bash
cp notes.txt notes_$(date +%Y-%m-%d).txt1
环境变量
bash
# 查看环境变量
env
export
# 常用环境变量
echo $HOME # 用户主目录
echo $USER # 当前用户名
echo $PATH # 命令搜索路径
echo $PWD # 当前工作目录
echo $SHELL # 当前 Shell
echo $RANDOM # 随机数(0-32767)
export MY_VAR="value"1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
Signals
Signals(信号)是 Linux 中的一种软件中断机制,用于进程之间或内核向进程发送通知。
当进程收到信号时,会:
- 暂停当前执行
- 处理信号(默认行为或自定义处理)
- 决定是否继续执行或退出
常见信号及作用
| 信号 | 含义 | 常见触发方式 |
|---|---|---|
| SIGINT | 中断进程 | Ctrl-C |
| SIGQUIT | 退出并生成 core dump | Ctrl-\ |
| SIGTERM | 请求优雅退出 | kill PID |
| SIGKILL | 强制终止(不可捕获) | kill -9 PID |
| SIGSTOP | 暂停进程 | kill -STOP PID |
| SIGTSTP | 终端暂停 | Ctrl-Z |
| SIGHUP | 终端关闭 | 关闭终端 |
常见命令
bash
bg # 恢复后台运行
fg # 恢复前台运行
jobs # 查看任务1
2
3
2
3
当关闭终端时:
- Shell 会发送 SIGHUP
- 所有子进程默认会退出
bash
nohup sleep 2000 &1
输出写入:
text
nohup.out1
nohup 会忽略 SIGHUP。