【发布时间】:2013-05-31 15:22:32
【问题描述】:
我正在编写一个 shell 脚本,我正在尝试检查命令的输出是否包含某个字符串。我想我可能必须使用 grep,但我不确定如何。有人知道吗?
【问题讨论】:
-
生成你要找的输出字符串后,命令是否需要继续运行,或者可以在那个时候立即关闭? (您的两个答案在这方面的语义不同)。
我正在编写一个 shell 脚本,我正在尝试检查命令的输出是否包含某个字符串。我想我可能必须使用 grep,但我不确定如何。有人知道吗?
【问题讨论】:
测试grep的返回值:
./somecommand | grep 'string' &> /dev/null
if [ $? == 0 ]; then
echo "matched"
fi
惯用的做法是这样的:
if ./somecommand | grep -q 'string'; then
echo "matched"
fi
还有:
./somecommand | grep -q 'string' && echo 'matched'
【讨论】:
= 是比较运算符,而不是 ==;见pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html
grep 'string' &>/dev/null 既不符合 POSIX 标准,而且执行起来比grep -q string 慢得多(如果string 出现在长输出流的早期)。 [需要注意的是,如果您想确保somecommand 即使在发出string 之后也能继续运行,在这种情况下使用grep -q——通过关闭其标准输入并在看到string 的第一个实例后退出——可能适得其反]。 (回复:“非 POSIX 兼容”,&> 是一个扩展 - 请参阅描述 POSIX 强制重定向支持的 pubs.opengroup.org/onlinepubs/009695399/utilities/…)。
matched吗?
测试$? 是an anti-pattern.
if ./somecommand | grep -q 'string'; then
echo "matched"
fi
【讨论】:
grep -Fxq F 代表固定(未解释),x 代表整行
$? 是反模式?
set -e 或 ERR 陷阱,测试 $? 不会将前面的命令设置为“已检查”,因此您的程序可以在以下情况下退出您希望它稍后简单地返回故意错误的路径。另一方面,$? 是易变的全局状态——很容易意外丢弃它的值。比如添加echo "Exit status is $?"这样的一行日志,$?中的新值变成echo的退出状态。
另一个选项是检查命令输出中的正则表达式匹配。
例如:
[[ "$(./somecommand)" =~ "sub string" ]] && echo "Output includes 'sub string'"
【讨论】:
一个干净的 if/else 条件 shell 脚本:
if ./somecommand | grep -q 'some_string'; then
echo "exists"
else
echo "doesn't exist"
fi
【讨论】:
以上所有(非常优秀的)答案都假设grep 可以“看到”命令的输出,这并不总是正确的:
SUCCESS 可以发送到 STDOUT 而 FAILURE 可以发送到 STDERR.
因此,根据您测试的方向,您的grep 可能会失败。也就是说,如果您正在测试 FAILURE 的情况,您必须在这种情况下使用2>&1 将命令的输出重定向到 STDOUT。
我使用grep 在 bash 脚本中进行了我认为非常简单的测试,但它一直失败。随之而来的是许多挠头。在我的脚本中使用set -x 表明该变量为空!所以我创建了以下测试来了解事情是如何破坏的。
注意:iscsiadm 是来自“open-iscsi”软件包的 Linux 工具,用于将主机连接/断开连接到 SAN贮存。命令iscsiadm -m session 用于显示是否建立了任何LUN 连接):
#!/bin/bash
set -x
TEST1=$(iscsiadm -m session)
TEST2=$(iscsiadm -m session 2>&1)
echo
echo 'Print TEST1'
echo $TEST1
echo
echo 'Print TEST2'
echo $TEST2
echo
如果 LUN WAS 已连接,则这两个变量都已成功填充值:
Print TEST1
tcp: [25] 192.168.X.XX:3260,1 iqn.2000-01.com.synology:ipdisk.Target-LUN1 (non-flash) tcp: [26] 192.168.X.XX:3260,1 iqn.2000-01.com.synology:storagehost.Target-LUN1 (non-flash)
Print TEST2
tcp: [25] 192.168.X.XX:3260,1 iqn.2000-01.com.synology:ipdisk.Target-LUN1 (non-flash) tcp: [26] 192.168.X.XX:3260,1 iqn.2000-01.com.synology:storagehost.Target-LUN1 (non-flash)
但是,如果 LUNWASN'T 连接,iscsiadm 将输出发送到 STDERR,并且只有“TEST2”变量填充到我们重定向到的位置标准输出使用2>&1;没有重定向到 STDOUT 的“TEST1”变量为空:
iscsiadm: No active sessions.
Print TEST1
Print TEST2
iscsiadm: No active sessions.
如果你有一个时髦的、半断的——在一个方向工作而不是另一个——这样的情况,试试上面的测试用你自己的命令替换iscsiadm,你应该得到重写测试以使其正常工作的适当可见性。
【讨论】: