目录
一、问题背景
在编写 Linux 安全检测脚本时,在处理一段代码逻辑的时候遇到一个有意思的问题,研究明白后整理出来。我在 shell 脚本中使用了如下结构:
suspicious_env=$(tr '\0' '\n' < "$proc_dir/environ" | grep -E "..." 2>/dev/null) || handle_error ...
以及:
suspicious_map=$(grep -E "..." "$proc_dir/maps" 2>/dev/null) || handle_error ...
目的是:
- 检测进程的可疑环境变量或内存映射(grep 匹配到说明检测项异常,反之说明检测项没问题)。
- 如果读取失败,调用
handle_error
报错并跳过当前进程。
但是发现:
即使没有错误,只是
grep
没有匹配内容(检测项正常),也会触发handle_error
。
二、问题本质
1. Shell 中的退出码行为
退出码 | 含义 | Shell 判断为成功? |
---|---|---|
0 | 成功(有匹配) | ✅ |
1 | 成功但无匹配 | ❌ |
2+ | 出现错误(如文件不存在) | ❌ |
2. ||
的逻辑行为
command || fallback
如果
command
返回非0
(即失败),就会执行fallback
所以:
grep
执行成功但没有匹配到内容时会返回1
,Shell 会当作失败处理,导致执行handle_error
- 但我只想在文件读取失败时才报错,而不是因为没有匹配内容就报错
三、解决方案
1. 分开处理“读取失败”和“没有匹配内容”
原理:
- 先读取文件内容到变量(如
env_content
或map_content
) - 判断读取是否失败(退出码非
0
),才调用handle_error
- 再用
grep
匹配内容,即使没有匹配,也不报错
示例:
env_content=$(tr '\0' '\n' < "$proc_dir/environ" 2>/dev/null)
if [ $? -ne 0 ]; then
handle_error ...
continue
fi
suspicious_env=$(echo "$env_content" | grep -E "...")
2. 使用变量代替多次读取文件
- 一次性读取
/proc/<pid>/environ
或/proc/<pid>/maps
到变量中 - 后续操作都基于变量,提高性能,减少系统调用
3. 处理正则表达式中的特殊字符
比如:
rwxp.*$$heap$$
-
在 Shell 中,
$$
会被解释为当前进程 PID,导致匹配失败 -
正确做法是:
grep -E '(rwxp.*$$heap$$)'
或:
grep -E 'rwxp.*$$heap$$'
用单引号包裹整个正则表达式,防止被 Shell 替换
四、优化后的写法(我最终采用)
环境变量检测(environ
)
env_content=$(tr '\0' '\n' < "$proc_dir/environ" 2>/dev/null)
if [ $? -ne 0 ]; then
handle_error 0 "读取进程 ${pid} 环境变量失败" "processInfo"
continue
fi
suspicious_env=$(echo "$env_content" | grep -E '(LD_PRELOAD|LD_LIBRARY_PATH.*\.so|ROOTKIT|HIDE)')
if [ -n "$suspicious_env" ]; then
proc_name=$(cat "$proc_dir/comm" 2>/dev/null || echo "unknown")
env_anomalies+=("PID:$pid Name:$proc_name 可疑环境变量")
fi
内存映射检测(maps
)
map_content=$(cat "$proc_dir/maps" 2>/dev/null)
if [ $? -ne 0 ]; then
handle_error 0 "读取进程 ${pid} 内存映射失败" "processInfo"
continue
fi
suspicious_map=$(echo "$map_content" | grep -E '(rwxp.*$$heap$$|rwxp.*$$stack$$|rwxp.*deleted)')
if [ -n "$suspicious_map" ]; then
proc_name=$(cat "$proc_dir/comm" 2>/dev/null || echo "unknown")
suspicious_maps+=("PID:$pid Name:$proc_name 可疑内存映射")
fi
五、总结一句话
不要直接对
grep
命令使用||
,除非你真的需要区分“有无匹配内容”。否则,应该先判断文件是否读取失败,再单独处理匹配内容。
by 久违