Bash:“fc-list”和“pipefail”的奇怪交互

问题描述 投票:0回答:1

我完全失去理智了。

假设我想回显单词

"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
输出会改变什么吗?每次它总是返回完全相同的字体列表。
有人可以解释一下这是怎么回事吗? 🫠

更重要的是:我该如何解决它?

提前致谢。

bash grep
1个回答
0
投票
fc-list

一旦找到匹配项就会退出。当它退出时,它会破坏管道:

grep
尚未完成发送所有内容。由于管道信号损坏 (
echo
),
echo
异常终止,因此失败。
您设置了

SIGPIPE

,这样整个管道就被认为失败了。

这是

pipefail

的缺点。当您遇到

pipefail
时,当且仅当
command | grep -q pattern
没有发生时,您通常希望失败。如果
pattern
失败了,那也没关系;如果它在失败之前产生了匹配,那么就满足了。
POSIX shell 及其前身是围绕此类用例设计的; Bash 为需要检查所有管道元素是否成功的情况引入了 

command

。如果第二个或后续管道元素中的任何一个突然退出,导致前一个管道破裂,它将无法工作。

    

© www.soinside.com 2019 - 2024. All rights reserved.