Linux · 2 分钟阅读
Shell 编程基础
2026 年视角:bash 仍是服务器端的事实标准;zsh 在 macOS 默认;fish 在开发者圈流行。生产脚本建议严格 bash(POSIX 兼容子集),个人
.bashrc/.zshrc爱用啥用啥。
Shell 类型
echo $SHELL # 当前 shell
cat /etc/shells # 系统可用
chsh -s /bin/bash # 切换登录 shell
脚本结构
#!/bin/bash
# 注释:脚本作用
set -euo pipefail # 严格模式(推荐)
VAR="value"
readonly CONST="locked"
function greet() {
local name="$1"
echo "Hello, $name"
}
greet "Django"
变量
| 类型 | 例子 | 说明 |
|---|---|---|
| 用户变量 | name="x" |
等号两边不能有空格 |
| 环境变量 | export PATH=... |
子进程可见 |
| 只读变量 | readonly name |
不能改 |
| 特殊变量 | $0 $1 $# $@ $? $$ $! |
脚本上下文 |
echo "$name" # 双引号解析变量
echo '$name' # 单引号原样
echo "${name}_bak" # 推荐花括号,避免歧义
条件
if [[ -f "$file" ]]; then
echo "file"
elif [[ -d "$dir" ]]; then
echo "dir"
else
echo "other"
fi
# case
case "$1" in
start) systemctl start app ;;
stop) systemctl stop app ;;
*) echo "Usage: $0 {start|stop}" ;;
esac
[[ ... ]] 是 bash 关键字,支持 && ||、正则;[ ... ] 是 POSIX,兼容性更好。
循环
for i in {1..5}; do echo "$i"; done
for f in *.log; do mv "$f" "old.$f"; done
# C 风格
for ((i=0; i<10; i++)); do echo "$i"; done
# while
while read -r line; do
echo "$line"
done < file.txt
# until
until systemctl is-active app; do sleep 1; done
函数
backup() {
local src="$1"
local dst="${2:-/backup}"
cp -a "$src" "$dst/$(basename "$src").$(date +%F)"
}
backup /etc/nginx
函数返回的是退出码(0-255),不是字符串。要回字符串用
echo,调用方result=$(func)。
字符串
${#s} # 长度
${s:0:5} # 截取
${s#prefix} # 去前缀(最短)
${s%suffix} # 去后缀(最短)
${s//old/new} # 全量替换
数组:
arr=(a b c)
echo "${arr[0]}"
echo "${arr[@]}"
arr+=(d)
文件测试
[[ -e $f ]] && echo "exists"
[[ -d $d ]] && echo "dir"
[[ -f $f ]] && echo "file"
[[ -r $f ]] && echo "readable"
[[ -x $f ]] && echo "executable"
文本三件套
grep -nE 'pattern' file.log
sed -i 's/old/new/g' file
awk '$3 > 100 {print $1}' file
调试
bash -n script.sh # 语法检查
bash -x script.sh # 打印执行过程
set -euo pipefail # 脚本头部加:遇错退出、变量未定义报错、管道失败
trap 'echo "failed at $LINENO"' ERR
一些经验
- 变量、命令替换都加双引号:
"$var"、"$(cmd)",避免空格炸裂 - 路径里有空格用
[[ ]]+ 引号,别信[ ] - 临时文件
mktemp创建,trap "rm -f $tmp" EXIT清理 - 写大段处理逻辑前先
set -euo pipefail,能挡 80% 的低级错 - 多用
shellcheck,CI 跑一次,比事后 debug 强