我完全失去理智了。
假设我想回显单词
"Sans"
,然后用grep
检查它是否包含子字符串"Sans"
或"CrazyStuff"
:
#!/bin/bash
echo Sans | grep -q Sans && echo y || echo n
echo Sans | grep -q CrazyStuff && echo y || echo n
set -o pipefail
echo Sans | grep -q Sans && echo y || echo n
echo Sans | grep -q CrazyStuff && echo y || echo n
正如预期的那样,
"Sans"
包含"Sans"
,并且不包含"CrazyStuff"
,因此无论pipefail
设置如何,
grep Sans
成功了grep CrazyStuff
失败了grep Sans
成功了grep CrazyStuff
失败了输出为:
y
n
y
n
到目前为止一切顺利。
现在,让我们用
"Sans"
的输出替换常量字符串 fc-list
(它列出了已安装的字体;您可能还会有一些 Sans-Serif 字体,因此它应该包含 Sans
):
#!/bin/bash
captured="$(fc-list)"
echo "$captured" | grep -q Sans && echo y || echo n
echo "$captured" | grep -q CrazyStuff && echo y || echo n
set -o pipefail
echo "$captured" | grep -q Sans && echo y || echo n
echo "$captured" | grep -q CrazyStuff && echo y || echo n
由于
$captured
输出包含子字符串 Sans
,因此该程序的行为与第一个程序完全相同,输出再次为:
y
n
y
n
现在,大胆的特技:我们只需调用
echo
四次,而不是对 $captured
的 fc-list
输出进行操作:fc-list
显然,它的行为方式必须与前两个示例完全相同,因此毫不奇怪,输出是
#!/bin/bash
fc-list | grep -q Sans && echo y || echo n
fc-list | grep -q CrazyStuff && echo y || echo n
set -o pipefail
fc-list | grep -q Sans && echo y || echo n
fc-list | grep -q CrazyStuff && echo y || echo n
...等等,什么
? 通过实际调用
y
n
n
n
来替换
$captured
的常量 fc-list
输出会改变什么吗?每次它总是返回完全相同的字体列表。有人可以解释一下这是怎么回事吗? 🫠
更重要的是:我该如何解决它?
提前致谢。
fc-list
一旦找到匹配项就会退出。当它退出时,它会破坏管道:
grep
尚未完成发送所有内容。由于管道信号损坏 (echo
),echo
异常终止,因此失败。您设置了SIGPIPE
,这样整个管道就被认为失败了。
这是pipefail
的缺点。当您遇到
pipefail
时,当且仅当 command | grep -q pattern
没有发生时,您通常希望失败。如果 pattern
失败了,那也没关系;如果它在失败之前产生了匹配,那么就满足了。POSIX shell 及其前身是围绕此类用例设计的; Bash 为需要检查所有管道元素是否成功的情况引入了 command
。如果第二个或后续管道元素中的任何一个突然退出,导致前一个管道破裂,它将无法工作。