Bash正则表达式与Grep使用遇阻,寻求技术协助
问题:使用grep提取Scenario测试块时意外包含注释占位符
我正在编写Bash脚本,需要从以下文本中提取所有Scenario测试块:
# #------------------------------------------- spaceholder --------------------------------------------------------------------------- # #@E2E-1 @id:1 Scenario: Login & Search: B2B_PKG_IN >> BE Given I am on the login page When I enter <username> and incorrect password multiple times Then I should be locked out of my account And I should see a lockout message #@E2E-2 @id:32 Scenario: Login & Search: B2B_PKG_IN >> NL Given I am on the login page When I enter <username> and incorrect password multiple times Then I should be locked out of my account And I should see a lockout message # #------------------------------------------- B2B_PKG_3PA --------------------------------------------------------------------------- # @E2E-3 @id:3 Scenario: Login & Search: B2B_PKG_3PA >> BE Given I am on the login page When I enter <username> and incorrect password multiple times Then I should be locked out of my account And I should see a lockout message
我用正则表达式((@[^\n]+|#@[^\n]+)?\s*)?Scenario:[^\n]*\n(?:[^\n]*\n)*?\n在在线正则测试工具中可以正确提取目标块,但执行grep -Pzo "$pattern" "$current_file"时,结果意外包含了开头的#------------------------------------------- spaceholder ...注释部分。尝试过grep -E、grep -oP、grep -Po等参数组合都无法解决。
原因分析
grep -z会将整个文件读取为单个NUL分隔的记录,原正则中的非贪婪匹配(?:[^\n]*\n)*?在这种模式下,可能因为注释块的连续换行触发意外匹配;同时原正则未明确排除纯注释行(不含#@的注释),导致grep从注释块开始匹配到后续的Scenario块。
修正后的解决方案
方案1:调整正则,明确匹配规则
使用更精确的正则,确保只匹配带有可选标签(@xxx或#@xxx)的Scenario块,且跳过纯注释行:
pattern='(^\s*(#@|@)[^\n]+\s*)?^\s*Scenario:[^\n]*\n(?:^\s+[^\n]+\n)*' grep -Pzo "$pattern" "$current_file" | tr '\0' '\n'
正则解释:
(^\s*(#@|@)[^\n]+\s*)?:匹配可选的标签行(以#@或@开头,允许前导空格),?表示该标签行非必需^\s*Scenario:[^\n]*\n:匹配Scenario行,允许前导空格(?:^\s+[^\n]+\n)*:匹配后续的步骤行(必须有前导空格,确保是Scenario的子步骤),重复匹配所有步骤行
参数说明:
-P:启用Perl兼容正则表达式-z:将文件视为NUL分隔的记录,支持跨行匹配-o:只输出匹配的部分tr '\0' '\n':将grep输出的NUL字符转换回换行,恢复正常文本格式
方案2:使用awk替代grep(更直观稳定)
如果正则在grep中容易出问题,用awk处理结构化文本更可靠:
awk '/^\s*(#@|@)/{tag=$0; next} /^\s*Scenario:/{if(tag!="")print tag; print; in_scenario=1; next} in_scenario && /^\s+/{print} in_scenario && /^\s*$/{in_scenario=0; print ""}' "$current_file"
逻辑解释:
- 匹配标签行(
#@或@开头),保存标签内容并跳过后续处理 - 匹配Scenario行,打印标签(如果存在)和Scenario行,标记进入Scenario块
- 在Scenario块内,匹配有前导空格的步骤行并打印
- 遇到空行时,退出Scenario块并打印空行分隔不同测试块
内容的提问来源于stack exchange,提问作者PopSmoke
相关产品推荐
相关产品推荐

