返回 方案一: 捕获标准输出,结合
方案二: 两次
方案三 (进阶): 结合
grep日志搜索权限问题与退出码处理技巧
Linux
2025-03-21 20:20:11
grep
搜索日志文件,权限不足导致退出码异常,如何正确判断?
开发脚本时遇到个麻烦事。脚本用来搜索指定目录下的日志文件,看有没有特定的内容。这个目录下既有普通日志文件,也有 gzip 压缩的日志文件。我的想法是,先搜普通日志,如果没找到,再去搜压缩包里的。有个限制:运行脚本的用户权限不够,读不了目录下的某些文件。
问题来了,grep
就算找到了内容,或者没找到内容,因为有权限问题,都会遇到 "Permission denied",最终退出码总是 2。 就像这样:
[user@mypc ~]$ grep -rnw '/usr/toto/log' -e "pattern"
grep: /usr/toto/log/privileged.log.2025-01-10: Permission denied
grep: /usr/toto/log/privileged.out.1: Permission denied
/usr/toto/log/testontargz.log:1:pattern
grep: /usr/toto/log/privileged.out.3: Permission denied
grep: /usr/toto/log/privileged.log.2025-01-17: Permission denied
grep: /usr/toto/log/privileged.out.2: Permission denied
/usr/toto/log/teston2.log:1:pattern
[user@mypc ~]$ echo $?
2
我晓得 grep -rnw**s**
可以隐藏 "Permission denied" 信息,但退出码该是 2 还是 2,没找到时并不会变成 1,找到也不会变成0。
我试过在 if
里用 $?
判断退出码是不是 1,但现在这情况肯定不行。所以我想,$?
可能不是正解,但我脚本水平有限... 大家给出出主意?
原脚本如下:
#!/bin/sh
printf '\n'
# Search for the error in not compressed log files
printf '%s\n' ">> Errors found in the log file(s):"
grep -rnws '/usr/toto/log' -e "$1"
# Only search in the compressed files if the above grep didn't output any actual result
if [ $? -eq 1 ]; then
# Search for the error in compressed log files
printf '\n'
pattern=$1; shift
for x do
if case "$x" in
*.gz|*.[zZ]) <"$x" gzip -dc | grep -q -e "$pattern";;
esac
then
printf '%s\n' ">> Error found in one of the log files that is stored in the file $x"
fi
done
fi
printf '\n'
调用脚本: myscript.sh patterntofind /usr/toto/log/compressedfile.*.gz
一、 问题原因
grep
命令遇到权限问题时,即使找到了匹配项,退出码也会是 2。 这干扰了脚本根据退出码判断是否继续搜索压缩文件的逻辑。$?
获取的是上一个命令的退出码,这里就是 grep
的退出码。而我们的需求是,仅当grep
在无权限错误干扰下,未输出任何匹配结果时,其退出码应为 1,才去搜索压缩文件。
二、 解决方案
方案一: 捕获标准输出,结合 grep
的 -q
选项
-
原理:
grep -q
:安静模式。不输出任何内容到标准输出,只返回退出码。 找到匹配项返回 0,没找到返回 1,发生错误(包括权限错误)返回 2。- 将
grep
的标准输出重定向到变量,标准错误重定向丢弃。 - 检查变量是否为空。如果为空,说明没有找到匹配项(即使有权限错误,也不会有匹配项输出到标准输出)。
-
代码示例:
#!/bin/sh
printf '\n'
# Search for the error in not compressed log files
printf '%s\n' ">> Errors found in the log file(s):"
output=$(grep -rnw '/usr/toto/log' -e "$1" 2>/dev/null) #将错误输出重定向到 /dev/null
# Only search in the compressed files if the above grep didn't output any actual result
if [ -z "$output" ]; then #检查捕获的输出是否为空
# Search for the error in compressed log files
printf '\n'
pattern=$1; shift
for x in "$@"; do # 修正:循环遍历剩余参数
if case "$x" in
*.gz|*.[zZ]) <"$x" gzip -dc | grep -q -e "$pattern";;
esac
then
printf '%s\n' ">> Error found in one of the log files that is stored in the file $x"
fi
done
fi
printf '\n'
- 改进说明:
2>/dev/null
:把grep
的标准错误输出重定向到空设备,避免干扰。-z "$output"
: 测试output
是否为空.for x in "$@"
: 循环使用更安全的写法。$@
代表了所有未被shift过的参数列表。
方案二: 两次 grep
,一次用于过滤,一次用于确认
-
原理:
- 第一次
grep
,不带-s
选项,将标准输出和标准错误合并 (&>
)。 - 第二次
grep
从第一次grep
的输出结果中,再次查找匹配项,并使用-q
- 判断第二次
grep
的退出码是否是0
.
- 第一次
-
代码示例:
#!/bin/sh
printf '\n'
# Search for the error in not compressed log files
printf '%s\n' ">> Errors found in the log file(s):"
first_grep_result=$(grep -rnw '/usr/toto/log' -e "$1")
# Only search in the compressed files if the above grep didn't output any actual result
# 使用管道和grep -q来判断第一次grep是否有真正的结果输出。
if ! echo "$first_grep_result" | grep -q -e "$1"; then
# Search for the error in compressed log files
printf '\n'
pattern=$1; shift
for x in "$@"; do # 修正:循环遍历剩余参数
if case "$x" in
*.gz|*.[zZ]) <"$x" gzip -dc | grep -q -e "$pattern";;
esac
then
printf '%s\n' ">> Error found in one of the log files that is stored in the file $x"
fi
done
fi
printf '\n'
- 改进说明:
- 利用管道和两个
grep
, 先拿到带错误的结果,再从这些结果中进行二次匹配,如果第二次匹配成功,退出码是0
. 如果不成功(仅有权限错误,或真的不匹配),退出码是非0
.
- 利用管道和两个
方案三 (进阶): 结合find
命令规避权限问题 (如果允许更改文件查找方式)
-
原理:
find
命令可以通过-readable
测试文件是否可读,从而一开始就避免grep
访问无权限文件。- 只对可读文件执行
grep
-
代码示例:
#!/bin/sh
printf '\n'
# Search for the error in not compressed log files
printf '%s\n' ">> Errors found in the log file(s):"
# 使用find查找可读文件,并对每个文件执行grep
find /usr/toto/log -type f -readable -print0 | while IFS= read -r -d $'\0' file; do
grep -Hn -e "$1" "$file" # 使用-H选项输出文件名
done | grep -q -e "$1" # 确保至少找到一次才不执行下面的压缩文件搜索
# Only search in the compressed files if no results were found above
if [ $? -ne 0 ];then
# Search for the error in compressed log files
printf '\n'
pattern=$1; shift
for x in "$@"; do #修正: 循环遍历剩余参数
case "$x" in
*.gz|*.[zZ])
if zgrep -q -e "$pattern" "$x"; then # zgrep 简化了压缩文件的搜索
printf '%s\n' ">> Error found in one of the log files that is stored in the file $x"
fi
;;
esac
done
fi
printf '\n'
-
改进和安全建议:
-print0
和read -d $'\0'
:用空字符分隔文件名,更安全地处理包含空格或其他特殊字符的文件名。while IFS= read -r -d $'\0' file
循环结构能够安全,完整处理各种文件名grep -Hn
:-H
选项强制输出文件名,即使只搜索一个文件。- 使用
zgrep
:直接搜索压缩文件,更简洁。 find
命令更精细, 可以指定-type f
表示查找文件,-readable
控制查找可读文件.- 最终的
grep -q
检查可以确认是否find + grep
真的有结果. 如果$? -ne 0
, 表示没结果。
方案三是最稳妥的, 通过find
控制可读文件范围, 直接避免了grep
触发权限错误, 后续处理更简单和准确.