在两个标记之间提取文本并不总是有效

在两个标记之间提取文本并不总是有效

所以,不久前我看到用于提取两个“标记”之间的文本的片段:

# Usage: extract file "opening marker" "closing marker"
    while IFS=$'\n' read -r line; do
        [[ "$extract" && "$line" != "$3" ]] &&
            printf '%s\n' "$line"

        [[ "$line" == "$2" ]] && extract=1
        [[ "$line" == "$3" ]] && extract=
    done < "$1"

(在这里,我只是随意将其从函数中删除并将其放入名为 的文件中extract)现在,它在“大多数”标记对上确实可以正常工作。但我注意到它并不总是有效:

按照原始代码片段的示例,使用 N 个重复字符(使用“#”而不是“`”,因为 SO 上存在格式错误):

###sh
test
###

这样做时有效extract file '###sh' '###',但如果我们使用以下标记:

###
test
###

和 do extract file '###' '###',然后它不起作用?

虽然我可以看到脚本中的条件确实评估正确(变量extract等于1使用时set -x)。

这是怎么回事?

PS:当然,我说“它不起作用”的意思是,当它不起作用时,它不会在实例中打印任何内容。

上面的两个示例输出不应该包含标记(只是在两个标记之间提取的文本)...

如果可能的话,我更喜欢 bash/shell 解决方案。

答案1

正如其他人在对您的问题的评论中所述,您的脚本不起作用,因为当[[ "$line" == "$2" ]]满足开始条件时,extract设置为 1,但在下一行[[ "$line" == "$3" ]]也满足结束条件,该脚本重置extract为空字符串。

这是您的固定脚本:

# Usage: extract file "opening marker" "closing marker"
while IFS=$'\n' read -r line; do
    if [ "$extract" ]; then
        if [[ "$line" == "$3" ]]; then
             extract=
        else
            printf '%s\n' "$line"
        fi
    elif [[ "$line" == "$2" ]]; then
        extract=1
    fi
done < "$1"

而且,如果您需要这个,根据@Freddy的建议,这里有一个稍微修改过的版本,要求在要打印的文本中存在结束标记:

# Usage: extract file "opening marker" "closing marker"
while IFS=$'\n' read -r line; do
    if [ "$extract" ]; then
        if [[ "$line" == "$3" ]]; then
            printf '%s\n' "${lines[@]}"
            lines=() extract=
        else
            lines+=( "$line" )
        fi
    elif [[ "$line" == "$2" ]]; then
        extract=1
    fi
done < "$1"

(行在数组中累积lines,仅在遇到结束标记时打印)

答案2

每当看到 $2 时,就向 extract 变量添加切换逻辑。感谢 xhiene 指出!

[[ $line == $2 ]] && case $extract in '') extract=1;; *) extract=; esac

现在删除对 extract 变量的 $3 依赖。

HTH。

相关内容