应根据脚本实际语法特性选择:若使用[[、$(())、数组、进程替换等bash特有功能,必须用#!/bin/bash;若仅用posix标准语法(如[、$((...))),则优先用#!/bin/sh以保证跨平台兼容性。

怎么判断一个脚本该用 #!/bin/bash 还是 #!/bin/sh
脚本第一行的 shebang 决定了执行器,不是随便写的。用错会导致语法报错或行为不一致,尤其在 Alpine、Debian 或 CI 环境里常见。
实际选法看内容:bash 支持数组、[[、source 参数扩展、进程替换等;sh 只保证 POSIX 兼容,连 echo -n 都可能失效。
- 用了
[[、$(())、declare -a、for ((i=0; i → 必须用 <code>#!/bin/bash - 只用
[、case、while read、简单变量赋值 →#!/bin/sh更安全,尤其部署到容器或嵌入式系统时 -
/bin/sh在不同系统可能指向 dash(Ubuntu)、ash(Alpine)或 bash(macOS),别假设它支持 bash 特性
读取用户输入时,read 怎么避免空格截断和换行丢失
read 默认按 IFS(空格/制表/换行)切分字段,直接 read var 会吃掉开头结尾空格,多词输入只存第一个词。
正确写法是加 -r 和引号,且明确指定 IFS:
- 读一行完整内容(含空格、反斜杠):
IFS= read -r line - 读多个字段但保留各自空格:
IFS='|' read -r field1 field2(用|分隔) - 从文件逐行处理时,别写
cat file | while read line—— 管道会开子 shell,循环外变量失效;改用while IFS= read -r line; do ... done
if 判断文件存在或非空,为什么 [ -f $file ] 常出错
变量未引号包裹 + 路径含空格/特殊字符 = 直接报 [: too many arguments 或静默失败。这不是语法问题,是 word splitting 导致的。
- 永远用
[ -f "$file" ],双引号不能省;$file为空时,[ -f ]会变成测试字符串-f是否非空,结果恒真 - 检查文件非空:
[ -s "$file" ](比[ -n "$(cat $file)" ]安全,后者会因权限失败而报错) - 判断命令是否成功,别依赖
if [ $? -eq 0 ]—— 直接if cmd; then更干净,也避免 $? 被中间 echo 覆盖
脚本里调用外部命令,路径和环境变量为什么总不生效
Shell 脚本默认不继承你的交互式 shell 的 PATH 或 alias,也不加载 ~/.bashrc。CI 或 crontab 下更明显。
- 别写
python script.py,先查真实路径:command -v python,或用绝对路径/usr/bin/python3 - 需要自定义 PATH?在脚本开头加
export PATH="/opt/mytools:$PATH",但注意:crontab 中默认 PATH 很窄(通常只有/usr/bin:/bin) - alias 不会在脚本中展开,必须用完整命令;函数可以定义,但别指望它自动加载
~/.bash_functions—— 要手动source
真正麻烦的是跨环境一致性:同一段脚本在本地能跑,放到 GitHub Actions Ubuntu runner 就缺命令,或者 Alpine 镜像里没有 gawk。写之前先确认目标环境的基础工具集,比事后 debug 省三倍时间。










