面向初步接触 Shell 脚本的同学,按常见开发场景整理核心语法。
一、脚本基础知识
1. 指定解释器
脚本首行通常写为:
#!/bin/bash
含义是“使用 Bash 执行当前脚本”。例如:
#!/bin/bash
echo "Hello Shell"
2. 注释写法
单行注释使用 #:
# 这是一行注释
echo "Hello World" # 行尾注释
Bash 没有官方多行注释语法,常见“模拟写法”如下:
: '
这是第一行注释
这是第二行注释
'
echo "不会执行注释里的内容"
3. 命令执行与拼接
常见拼接方式:
# 无论 command1 成功与否都执行 command2
command1 ; command2
# command1 成功才执行 command2
command1 && command2
# command1 失败才执行 command2
command1 || command2
示例:
cd /tmp && touch test.txt && ls -l test.txt
cp file.txt /dest || echo "复制失败"
4. 输入输出与重定向
echo 用于输出,重定向用于写入或读取文件:
echo "简单输出"
echo -e "第一行\n第二行"
echo "新内容" > test.txt
echo "新增内容" >> test.txt
5. 统计行数
在统计行数时,wc -l 很常用:
wc:word count,统计信息-l:line,按行计数
wc -l < test.txt
二、变量与参数
1. 变量定义与使用
变量定义时,等号两边不能有空格:
name="Shell脚本"
age=20
echo "名称:$name,年龄:$age"
echo "文件名:${name}_script.sh"
只读变量与删除变量:
readonly version="1.0"
unset name
2. 环境变量与常见系统变量
环境变量可通过 export 传递给子进程:
export PATH="$PATH:/new/bin"
export PROJECT_DIR="/home/user/project"
常见系统变量:
$HOME:当前用户家目录$PWD:当前工作目录$USER:当前用户名$?:上一条命令退出状态(0 为成功)$$:当前进程 ID
示例:
echo "用户:$USER,目录:$HOME"
ls /nonexist_dir || echo "退出状态:$?"
3. 脚本参数传递
位置参数:
#!/bin/bash
echo "脚本名:$0"
echo "第一个参数:$1"
echo "第二个参数:$2"
特殊参数:
$#:参数总数$*:所有参数(作为一个整体)$@:所有参数(保留参数边界,循环中更常用)
#!/bin/bash
echo "参数总数:$#"
echo "所有参数(\$*):$*"
echo "所有参数(\$@):$@"
for param in "$@"; do
echo "参数:$param"
done
三、流程控制
1. 条件判断(if / elif / else)
if 条件; then
echo "条件成立"
elif 其他条件; then
echo "命中其他分支"
else
echo "条件不成立"
fi
常见判断速查:
- 文件:
-f(普通文件)、-d(目录)、-e(存在) - 字符串:
=、!=、-z(空)、-n(非空) - 数值:
-eq、-ne、-gt、-lt、-ge、-le
示例:
file_path="test.txt"
if [[ -e $file_path ]]; then
echo "文件存在"
else
echo "文件不存在"
fi
2. 循环结构(for / while / until)
for 列表遍历:
fruits=("apple" "banana" "orange")
for fruit in "${fruits[@]}"; do
echo "水果:$fruit"
done
for C 风格循环:
for ((i=1; i<=5; i++)); do
echo "循环次数:$i"
done
while 读取文件:
file="test.txt"
while read -r line; do
echo "行内容:$line"
done < "$file"
until(条件为假时继续):
count=1
until [[ $count -gt 3 ]]; do
echo "计数:$count"
((count++))
done
3. 分支匹配(case)
适合处理多分支命令参数:
case $1 in
start)
echo "启动服务..."
;;
stop)
echo "停止服务..."
;;
restart)
echo "重启服务..."
;;
*)
echo "无效参数,支持 start/stop/restart"
;;
esac
四、函数定义与使用
1. 基本结构
function say_hello {
echo "Hello from function!"
echo "第一个参数:$1"
}
say_hello "参数1"
2. 参数与返回值
Shell 函数通常通过标准输出“返回结果”:
function add {
local result=$(( $1 + $2 ))
echo "$result"
}
sum=$(add 5 3)
echo "总和:$sum"
return 返回的是状态码(0~255):
function check_file {
if [[ -f $1 ]]; then
return 0
else
return 1
fi
}
check_file "test.txt"
if [[ $? -eq 0 ]]; then
echo "文件存在"
else
echo "文件不存在"
fi
3. 作用域
Bash 变量默认是全局变量,函数内推荐使用 local 限制作用域:
global_var="全局变量"
function test_scope {
local local_var="局部变量"
echo "函数内访问全局变量:$global_var"
echo "函数内访问局部变量:$local_var"
global_var="函数内修改全局变量"
}
test_scope
echo "外部访问全局变量:$global_var"
五、常用高级能力
1. 进程管理(& / nohup / wait)
后台执行 &:
./script.sh &
echo "后台任务 PID: $!"
nohup 防止进程因终端关闭而终止:
nohup ./long_running_script.sh > output.log 2>&1 &
# nohup: 不受挂起信号影响
# 2>&1: 标准输出和错误输出都重定向到 output.log
# &: 放到后台运行
wait 等待后台任务完成:
./task1.sh &
./task2.sh &
wait # 等待所有后台任务完成
echo "所有任务结束"
等待指定后台进程:
./script.sh &
PID=$!
./other_script.sh
wait $PID
echo "指定进程执行完毕"
2. 数组操作
arr=("a" "b" "c")
arr[3]="d"
echo "第一个元素:${arr[0]}"
echo "所有元素:${arr[@]}"
echo "元素个数:${#arr[@]}"
for element in "${arr[@]}"; do
echo "数组元素:$element"
done
3. 正则匹配(=~)
email="[email protected]"
if [[ $email =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
echo "邮箱格式合法"
else
echo "邮箱格式不合法"
fi
4. 文本搜索(grep)
基础用法:
grep "keyword" file.txt # 在文件中搜索关键词
grep -i "keyword" file.txt # 忽略大小写
grep -n "keyword" file.txt # 显示行号
grep -v "keyword" file.txt # 反向匹配,显示不包含关键词的行
常用选项:
grep -r "keyword" ./dir/ # 递归搜索目录下所有文件
grep -c "keyword" file.txt # 统计匹配行数
grep -e "pattern1" -e "pattern2" file.txt # 多个模式匹配
grep -E "error|warning" log.txt # 扩展正则匹配多个模式
grep -o "pattern" file.txt # 只输出匹配部分
配合管道使用:
ps aux | grep "nginx" # 查找 nginx 进程
dmesg | grep -i "error" # 查找系统错误
cat log.txt | grep "2024-01-01" # 查找特定日期日志
5. 调试技巧
打印执行过程:
bash -x script.sh
错误捕获:
trap 'echo "执行到第 $LINENO 行出错,错误状态:$?"' ERR
echo "正常执行"
undefined_command
echo "不会执行到这里"
[!WARNING]
rm -rf、批量覆盖写入(>)等高风险操作,建议先在测试目录验证,再用于生产环境。
六、一些建议
- 开头统一写
#!/bin/bash,并保持脚本可执行权限。 - 涉及变量时尽量加双引号,减少空格和通配符引发的问题。
- 参数转发优先使用
"$@",避免参数边界被破坏。 - 对关键命令检查退出状态,必要时及时
exit 1。 - 使用
bash -x、trap ... ERR组合快速定位问题。
ShellBash新手入门