《Bash 脚本技巧:循环、算术与参数处理》
写 Shell 脚本常需要循环、条件判断、算术运算、参数解析与模板化输出。本文汇总这些高频写法,并给出可直接套用的参考脚本。
1 循环
1.1 for
# 方式一:空格分隔列表
for i in 1 2 3 4 5; do echo "Welcome $i times"; done
# 方式二:序列 {1..5}
for i in {1..5}; do echo "Welcome $i times"; done
# 方式三:seq 生成序列(起始 1,步长 2,到 20)
for i in $(seq 1 2 20); do echo "Welcome $i times"; done
# 方式四:C 风格
for (( c=1; c<=5; c++ )); do echo "Welcome $c times"; done
无限循环:
for (( ; ; )); do echo "press CTRL+C to stop"; sleep 1; done
1.2 while
x=1
while [ $x -le 5 ]; do
echo "Welcome $x times"
x=$((x+1))
if [ $x -eq 3 ]; then break; fi
done
数值比较运算符:-eq / -ne / -lt / -le / -gt / -ge。字符串比较用 = 与 !=。
2 算术运算
Shell 本身不支持浮点数运算,需借助 bc/awk。整数运算有以下三种写法:
# 1) let
let a=5+4; echo $a # 9
let "a = $1 + 30" # 支持引用命令行参数
# 2) expr(运算符两侧必须有空格,乘号需转义)
expr 5 \* 4 # 20
a=$(expr 10 - 3) # 7
# 3) $((...)),推荐,最常用
a=$(( 4 + 5 )); echo $a # 9
b=$(( a + 3 )); echo $b # 11(引用变量可省略 $)
(( b += 3 )); echo $b # 自增
浮点运算使用 bc:
n1=1; n2=3
# scale=3 表示保留 3 位小数
echo "scale=3; $n1 / $n2" | bc
字符串长度:
a='Hello World'
echo ${#a} # 11
3 命令行参数处理
脚本中 $1、$2…$#(参数个数)、$0(脚本名)。判断参数个数:
if [ $# -ge 1 ]; then
if [ "$1" = hello ]; then echo hello; fi
fi
if [ $# -ge 2 ]; then
if [ "$2" = world ]; then echo world; fi
fi
使用 getopts 风格的手动解析(支持长选项 -e/--extension):
POSITIONAL=()
while [[ $# -gt 0 ]]; do
case $key in
-e|--extension)
EXTENSION="$2"; shift; shift ;;
-s|--searchpath)
SEARCHPATH="$2"; shift; shift ;;
-l|--lib)
LIBPATH="$2"; shift; shift ;;
--default)
DEFAULT=YES; shift ;;
*)
POSITIONAL+=("$1") # 未知参数先存起来
shift ;;
esac
done
set -- "${POSITIONAL[@]}" # 恢复位置参数
echo "EXTENSION=$EXTENSION SEARCHPATH=$SEARCHPATH"
4 生成随机字符串
使用 /dev/urandom 与 tr 组合生成指定长度、指定字符集的随机串(常用于生成密码、token、随机 ID):
# 输出末尾自动加换行
< /dev/urandom tr -dc a-z0-9- | head -c8; echo
参数说明:tr -dc a-z0-9- 表示只保留 a-z/数字/-(-d 删除 + -c 补集的意思,即删除字符集之外的所有字符);head -c 8 取前 8 个字节。
另一种组合生成密码:
date +%s | sha256sum | base64 | head -c 32 | tr A-Z a-z | head -c8; echo
配合关联数组生成指定 TLD 的随机域名:
declare -A tld
tld=([0]=".cn" [1]=".org" [2]=".com")
cnt=0
while [ $cnt -le 10 ]; do
name=$(< /dev/urandom tr -dc a-z0-9 | head -c8; echo)
zone=${name}${tld[$(( cnt % 3 ))]}
echo "$zone" >> data.txt
cnt=$((cnt+1))
done
提示:MacOS 的 bash 3.2 不支持关联数组,需升级到 bash 4 及以上(如
brew install bash)。
5 heredoc 多行 JSON 与变量替换
脚本中构造多行 JSON,同时支持变量替换,可用 here-doc:
zone=example.com
zone_id=a115f71ab0398653f8f20001
data=$(cat << EOF
{
"zone": "${zone}",
"enable": true,
"zone_id": "${zone_id}"
}
EOF
)
curl -X POST -d "$data" -H"Content-type: application/json" http://your.server:8089/v1/api
解析返回 JSON(利用 python):
curl -s 'https://api.example.com/users/lambda' | python -c "import sys, json; print(json.load(sys.stdin)['name'])"
6 多行注释与模板
用 : '…' 包裹多行注释:
echo hello
: '
This is a
multiline comment
in bash
'
一个通用服务启停脚本(start/stop/restart/status):
#!/bin/bash
set -e
CWD="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
APP_NAME="myapp"
PID_FILE="${CWD}/${APP_NAME}.pid"
get_pid() { cat "$PID_FILE" 2>/dev/null || true; }
case "$1" in
start)
if [ -n "$(get_pid)" ] && kill -0 "$(get_pid)" 2>/dev/null; then
echo "${APP_NAME} is running"; exit 1
fi
nohup ./"${APP_NAME}" > info.log 2> error.log &
echo $! > "$PID_FILE"
echo "$APP_NAME started"
;;
stop)
pid=$(get_pid)
[ -n "$pid" ] && kill -9 "$pid" && rm -f "$PID_FILE" || echo "no $APP_NAME running"
;;
restart)
"$0" stop; sleep 1; "$0" start ;;
status)
pid=$(get_pid)
[ -n "$pid" ] && kill -0 "$pid" 2>/dev/null && echo "running (pid $pid)" || echo "not running"
;;
*) echo "Usage: $0 {start|stop|status|restart}" >&2; exit 1 ;;
esac
7 Shell 格式化工具 shfmt
使用 shfmt 统一 bash 脚本格式,避免手工对齐(Go 编写,go get mvdan.cc/sh/cmd/shfmt):
shfmt -w hello.sh # 原地格式化
shfmt -d hello.sh # 输出差异
shfmt -i 4 hello.sh # 4 空格缩进(默认 tab)
8 Alias 与简化登录
用 alias + 函数简化带参数的别名(例如带密钥登录远程主机):
# ~/.bashrc
alias sm='function _sm(){ ssh -i ~/.ssh/id_rsa user@$1; }; _sm'
source ~/.bashrc
# 之后即可 sm <ip> 直接登录到指定机器
9 常用变量与字符串操作
- 取长度:
${#var} - 默认值:
${var:-default}(未定义时取默认值) - 删除前缀:
${var#prefix_}、删除后缀:${var%_suffix} - 将分隔符替换为空格再转数组:
arr=(${str//,/ })
阅读 —
·
全站 —