Linux Shell 脚本踩坑总结:我被这些问题坑了3次后终于搞懂了

说实话,之前我总觉得Shell脚本嘛,不就是写几行命令嘛,能有多难?结果现实给了我当头一棒——一个看似简单的自动化脚本,硬是折腾了我一下午,各种奇怪的报错层出不穷。后来我才发现,Shell脚本里的坑是真的多,一个不留神就会踩坑。今天就把我的血泪教训整理出来,希望你们别再重复踩我踩过的坑。

坑1:变量赋值时的空格,让我脚本直接罢工

第一次写自动化脚本时,我想给变量赋值,结果脚本直接报错了。

问题现场:

#!/bin/bash
# 错误的写法
var = "hello"
echo $var

报错信息:

var: command not found

原因分析:

很多人习惯在 = 号两边加空格,觉得这样更"规范"。但在Shell中,= 两边绝对不能有空格var = "hello" 会被shell解释成命令 var,参数是 ="hello",所以会报 “command not found”。

正确写法:

#!/bin/bash
# 正确的写法
var="hello"
echo $var

# 或者加引号(推荐)
name="shell_script"
echo "脚本名称: $name"

注意事项:

  • 变量赋值时,= 前后不能有空格
  • 变量名通常用小写,加下划线分隔
  • 字符串推荐加双引号,防止空格问题

坑2:for循环处理带空格的文件名,文件竟然"消失"了

有一次我需要遍历目录下的所有文件,写了这么一段脚本:

问题现场:

#!/bin/bash
for file in $(ls /tmp/test/)  # 错误写法
do
    echo "处理文件: $file"
    # 假设这里有更复杂的处理
done

如果目录中有文件名为 “my document.txt”(带空格),这个脚本会把文件名拆成 “my” 和 “document.txt” 两个"文件"来处理,导致实际文件找不到。

原因分析:

$(ls ...) 会按空格分割输出,每遇到一个空格就认为是一个新的"文件"。这不是Shell的问题,是我们自己的写法有问题。

正确写法:

#!/bin/bash
# 方法1:使用 for in 直接匹配
for file in /tmp/test/*
do
    echo "处理文件: $file"
done

# 方法2:更安全的方式,设置IFS
#!/bin/bash
IFS=$'\n'  # 设置只按换行分割
for file in $(ls /tmp/test/)
do
    echo "处理文件: $file"
done
unset IFS  # 用完后恢复

# 方法3:find + while(推荐,处理复杂场景)
find /tmp/test/ -type f -name "*.txt" | while read file; do
    echo "处理文件: $file"
done

经验总结:

  • 尽量避免使用 $(ls ...) 来获取文件列表
  • 使用通配符 * 直接匹配文件
  • 复杂场景用 find + while read 组合

坑3:字符串比较不加引号,脚本总是判断错误

有一次我需要判断用户输入是否为空,写了这样的判断:

问题现场:

#!/bin/bash
read -p "请输入用户名: " username

if [ $username = "admin" ]  # 错误写法
then
    echo "欢迎管理员"
else
    echo "普通用户"
fi

如果用户直接按回车(输入为空),脚本会报错:

[: =: unary operator expected

原因分析:

$username 为空时,[ $username = "admin" ] 会变成 [ = "admin" ],这是非法的。Shell无法理解 = 前面没有操作数。

正确写法:

#!/bin/bash
read -p "请输入用户名: " username

# 方法1:给变量加双引号
if [ "$username" = "admin" ]; then
    echo "欢迎管理员"
else
    echo "普通用户"
fi

# 方法2:使用 [[ ]](更安全,支持空字符串)
if [[ "$username" = "admin" ]]; then
    echo "欢迎管理员"
else
    echo "普通用户"
fi

# 方法3:检查字符串是否为空
if [ -z "$username" ]; then
    echo "用户名不能为空"
    exit 1
fi

注意事项:

  • 变量比较时,变量一定要加双引号
  • 推荐使用 [[ ]] 替代 [ ],功能更强大
  • 记得检查空字符串情况(-z-n

坑4:整数比较用错了符号,逻辑完全相反

有一次我写了一个检查进程数量的脚本,结果判断逻辑完全反了:

问题现场:

#!/bin/bash
count=$(ps -ef | grep nginx | grep -v grep | wc -l)

# 错误的写法
if [ $count = 0 ]; then
    echo "Nginx未运行,启动它"
    systemctl start nginx
else
    echo "Nginx已在运行"
fi

等等,这个逻辑看起来没问题啊?但是如果 count 不是数字而是字符串,或者比较符号用错了,就会出问题。

原因分析:

在Shell中,=字符串比较-eq 才是整数比较。如果你用 = 比较数字,Shell会按字符串字典序比较,比如 10 < 2 会返回 true(因为 “1” < “2”)。

正确写法:

#!/bin/bash
count=$(ps -ef | grep nginx | grep -v grep | wc -l)

# 整数比较,使用 -eq
if [ "$count" -eq 0 ]; then
    echo "Nginx未运行,启动它"
    systemctl start nginx
else
    echo "Nginx已在运行,当前进程数: $count"
fi

# 其他整数比较符号:
# -eq equal 等于
# -ne not equal 不等于
# -gt greater than 大于
# -lt less than 小于
# -ge greater or equal 大于等于
# -le less or equal 小于等于

常见错误对比:

# ❌ 错误:数字比较用 =
if [ $num = 0 ]; then

# ✅ 正确:数字比较用 -eq
if [ $num -eq 0 ]; then

# ❌ 错误:字符串比较用 -eq
if [ $str -eq "yes" ]; then

# ✅ 正确:字符串比较用 =
if [ "$str" = "yes" ]; then

坑5:命令执行失败还在继续,脚本结果完全不对

我写过一个小工具,用于批量处理日志文件:

问题现场:

#!/bin/bash
# 压缩7天前的日志
find /var/log -name "*.log" -mtime +7 | while read file; do
    gzip "$file"
    echo "已压缩: $file"
done

# 后续操作
echo "清理完成,发送通知"
send_email "清理完成"

问题在于:如果 gzip 命令执行失败(比如权限问题),脚本不会报错,继续执行下去,你可能根本不知道有文件没处理成功。

原因分析:

默认情况下,Shell脚本中的命令失败不会停止脚本执行,也不会通知你。每个命令都有返回值(0表示成功,非0表示失败),但默认被忽略了。

正确写法:

#!/bin/bash
# 方法1:使用 set -e,遇到错误立即退出
set -e

find /var/log -name "*.log" -mtime +7 | while read file; do
    gzip "$file"
    echo "已压缩: $file"
done

echo "清理完成"

# 方法2:使用 || 捕获错误
find /var/log -name "*.log" -mtime +7 | while read file; do
    gzip "$file" || echo "压缩失败: $file"
done

# 方法3:使用 set -o pipefail,管道中任何一个命令失败都报错
set -o pipefail

# 方法4:检查命令返回值
gzip "$file"
if [ $? -ne 0 ]; then
    echo "压缩失败: $file"
    exit 1
fi

# 方法5:使用 trap 捕获错误并清理
cleanup() {
    echo "发生错误,清理临时文件"
    rm -f /tmp/temp_*
}
trap cleanup ERR

最佳实践:

  • 脚本开头加上 set -euo pipefail
    • -e:命令失败立即退出
    • -u:使用未定义变量报错
    • -o pipefail:管道中任何一个命令失败都报错
  • 重要操作后检查返回值
#!/bin/bash
set -euo pipefail

# 你的脚本逻辑

写在最后

写Shell脚本这么多年,我最大的感悟就是:看似简单的东西,反而最容易出错。变量赋值、循环、比较、条件判断,这些基础语法里的坑一个接一个。

给新手几点建议:

  1. 变量赋值不加空格,等号两边紧紧挨着
  2. 字符串比较加引号,防止空字符串报错
  3. 数字比较用 -eq 这些专门的符号,别用 =
  4. 处理文件名用通配符,别用 ls 管道
  5. 脚本开头加 set -euo pipefail,让错误无所遁形

Shell脚本是程序员的必备技能,这些坑早点避开,以后的路才能走得更顺。如果觉得有帮助,点个赞收藏一下,下次遇到类似问题就不怕了。


相关标签:#Linux #Shell #脚本 #运维 #踩坑记录 #自动化

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐