R / Richie全部文章 ↑

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 强

参考