shell脚本首行应写#!/usr/bin/env bash而非#!/bin/bash以提升跨平台兼容性;所有变量引用必须加双引号;推荐使用$(...)替代反引号;建议添加set -euo pipefail增强健壮性。

shell 脚本第一行必须是 #!/usr/bin/env bash,不是 #!/bin/bash
很多脚本在本地跑得好,一上 CI 或不同发行版就报 command not found,根源常在这行。用 /bin/bash 依赖绝对路径,但 Alpine 用 /bin/sh,macOS 的 Homebrew bash 是 /opt/homebrew/bin/bash,硬写路径等于自设兼容性障碍。
用 #!/usr/bin/env bash 让系统通过 $PATH 查找,更可靠。但要注意:env 本身也得在 /usr/bin/ 下(绝大多数系统都满足);如果脚本需要严格限定 bash 版本(比如用了 4.4+ 的 declare -v),那就得额外检查 bash --version,不能只靠 shebang。
- 别用
#!/bin/sh写 bash 特性(如数组、[[、source的-r参数),会静默失败或行为异常 - CI 环境里,Docker 镜像若精简(如
alpine:latest),默认没装 bash —— 此时要么换#!/bin/sh并重写逻辑,要么显式apk add bash - 脚本开头加
set -euo pipefail,避免变量未定义、命令失败被忽略、管道中间出错不退出等问题
变量引用不加双引号是 shell 脚本最常见崩溃点
写 cp $SRC $DST 看似简洁,一旦 $SRC 含空格、通配符或换行符,就会触发单词拆分和路径展开,轻则复制错文件,重则删库跑路。所有变量引用必须包在双引号里:cp "$SRC" "$DST"。
例外极少:比如明确要让 shell 展开通配符(ls $PATTERN),或要拆分成多个参数(cmd $ARGS),但这时应改用数组:args=(--verbose --input "$file"); cmd "${args[@]}"。
本书全面介绍PHP脚本语言和MySOL数据库这两种目前最流行的开源软件,主要包括PHP和MySQL基本概念、PHP扩展与应用库、日期和时间功能、PHP数据对象扩展、PHP的mysqli扩展、MySQL 5的存储例程、解发器和视图等。本书帮助读者学习PHP编程语言和MySQL数据库服务器的最佳实践,了解如何创建数据库驱动的动态Web应用程序。
-
[ -n $VAR ]错 —— 若$VAR为空,变成[ -n ],条件恒真;应写[ -n "$VAR" ] -
for f in $FILES错 —— 若$FILES="a b c",会循环三次,但若含空格路径("my file.txt")就裂开;应改用for f in $FILES前先设IFS=$'\n',或直接用数组 - 函数传参也要引号:
myfunc "$1" "$2",否则myfunc "a b" "c"进入函数后$1变成a,$2变成b
用 $(...) 替代反引号 `...`,且避免无必要子 shell
反引号嵌套困难(`echo \`date\``)、视觉混淆、且 POSIX 兼容性差;$(...) 更易读、支持嵌套($(echo $(date))),所有现代 shell 都支持。
但别滥用:每次 $(...) 都启一个子 shell,频繁调用 date 或 jq 会影响性能。如果只是取当前时间戳,一次就够了:ts=$(date +%s),后面复用 $ts。
- 不要写
if [ "$(grep -q foo file)" ]; then——grep -q本身就有退出码,直接if grep -q foo file; then更高效、更安全 -
$(cat file)应简化为$((bash/zsh 支持),少一次进程开销 - 解析 JSON 别用
$(jq '.name' data.json)多次 —— 一次性读进变量:data=$(jq -r '{name,age}' data.json),再用name=$(echo "$data" | jq -r '.name')(或直接用read+jq -r流式处理)
调试时别只靠 echo,用 set -x 和 PS4 定制输出
set -x 打印每条执行命令及其展开后的结果,比零散 echo "DEBUG: $var" 更系统。但默认输出太吵,尤其带长路径或敏感变量时。
用 PS4='+ ${BASH_SOURCE##*/}:${LINENO}: ' 可让调试行带上文件名和行号,快速定位;再配合 set -o functrace,还能追踪函数调用链。
- 临时调试加
set -x,完事加set +x关闭,别留在生产脚本里 - 敏感值(密码、token)打印前先用
local masked=${TOKEN:0:4}...处理,或改用declare -g+set -v配合日志分级 - 远程执行脚本(如
ssh host 'bash -s' )时,<code>set -x输出会混在 stdout 中,建议重定向:set -x 2> /tmp/debug.log
if 前下意识多按两个键打上双引号,或者看到反引号就本能地删掉重写。习惯比语法更难改。









