所以,不久前我看到这用于提取两个“标记”之间的文本的片段:
# 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。