【问题标题】:How to extract only the first instance of a number of lines between two strings in bash?如何仅提取bash中两个字符串之间的多行的第一个实例?
【发布时间】:2020-07-25 03:38:11
【问题描述】:

我的文件是:

abc
123
xyz
abc
675
xyz

我想提取:

abc
123
xyz

(123可以是任何东西,关键是我想要第一次出现)

我试过用这个:

sed -n '/abc/,/xyz/p' filename

但这给了我所有的实例。我怎么能得到第一个?

【问题讨论】:

  • 不要使用范围表达式 (/start/,/end/),因为它们只会使琐碎的任务比使用标志稍微简单一些,然后当您的需求发生轻微变化时需要完全重写或复制条件。只需使用一个标志(这意味着您不能使用 sed - 哦,好吧)表明您何时处于由您的第一个条件为真开始的块中。
  • 如果在匹配abc 的行之后没有匹配xyz 的行,那么预期的输出是什么?
  • 如果abc 在第一个xyz 之前出现两次,预期的输出是什么?
  • 有趣的问题。我应该自己提到它。 1、在xyz之前没有出现abc两次的可能性。一个 maven 检查样式插件确保 2. 如果没有匹配的行,则“预期”没有输出,只要它是确定的,我会用我得到的任何东西。

标签: shell awk sed


【解决方案1】:

您能否尝试使用所示示例进行以下、编写和测试。

awk '/abc/{found=1} found; /xyz/ && found{exit}'  Input_file

或根据 Ed sir 的评论以提高效率,请尝试关注。

awk '/abc/{found=1} found{print; if (/xyz/) exit}'  Input_file

说明:为上述添加详细说明。

awk '               ##Starting awk program from here.
/abc/{              ##checking condition if a line has abc in it then do following.
  found=1           ##Setting found here.
}
found;              ##Checking condition if found is SET then print that line.
/xyz/ && found{     ##Checking if xyz found in line and found is SET then do following.
  exit              ##exit program from here.
}
'  Input_file       ##Mentioning Input_file name here.

【讨论】:

  • @VyomMaitreya,欢迎您,很高兴在这个伟大的网站上帮助欢呼和快乐学习。
【解决方案2】:

如果你不介意 Perl:

perl -ne 'm?abc?..m?xyz? and print' file

将仅打印匹配的第一个块。匹配的分隔符必须是 ? 字符。

【讨论】:

  • 是在找到第一个块后退出还是继续读取它之后的所有行?我真的不知道,因为我不熟悉 perl 语法以及各种选项如何影响脚本的含义/作用。
  • @EdMorton 是的,'m?PATTERN?..m?PATTERN?'在第一个块之后停止匹配。这种一次性匹配在perlop 中有更详细的描述。
  • 但它仍然读取文件的其余部分,不是吗?就是不打印?
  • @PaulHodges 是的,它仍然读取文件。 range (...) 运算符在与第一个操作数匹配时变为真,并保持真直到第二个操作数为真。使用? 作为匹配分隔符将操作限制为一次匹配,除非使用reset 运算符(当检测到第一个文件上的EOF 时处理新文件。说...and print 是' if (...) {print}`.
  • 我宁愿看到它中止阅读超过我们知道我们已经完成的点。挑剔。 :)
【解决方案3】:

使用sed 你可以做到:

sed -n '/abc/,/xyz/p; /xyz/q' filename

q 将在达到“xyz”模式后退出。

【讨论】:

  • 在“abc”之前出现“xyz”的文件中,这可能会导致您不想要的东西。
  • @kvantour 但这不是 OP 提供的示例。
  • @Todd 是的,但我们更喜欢 shell 相关标签中的万无一失的答案
  • OP 没有解决 3 个案例 - 1) xyz 在第一个 abc 之前,2) 在 abc 之后没有 xyz,以及 3) 2 abc在第一个 xyz 之前。我们可以讨论我们认为 OP 会如何处理 2 和 3,但毫无疑问应该如何处理 1(忽略它),而且处理起来很简单,所以一个合理的答案就可以了。
  • 你可以使用sed '/abc/,$! d ; /xyz/ q' input来避免重复模式并在abc之前处理xyz
【解决方案4】:

匹配终端条件两次

不管是哪种语言,面向行的处理最常用的技术是打印给定范围内的行,然后使用第二个命令在达到终端条件时退出循环。这对于 sed、awk、ruby 和 perl 中的常见模式是正确的,尽管当然还有其他技术可以使用多行匹配来执行(不使用保持空间的 sed 不支持)。例如,您可以使用非贪婪的多行正则表达式模式,例如 /^abc\n.*?\nxyz$/m

为了更详细地说明您想要的面向行的方法,请考虑这个 Ruby 单行,其中$_ 保存当前输入行。从外壳:

$ ruby -ne 'puts $_ if /^abc$/ .. /^xyz$/; exit if /^xyz/' filename 
abc
123
xyz

sed中的等价物是:

$ sed -n '/^abc$/,/^xyz$/p; /^xyz$/q' filename
abc
123
xyz

您所缺少的只是附加到 secondxyzfirst 实例匹配的退出或退出命令。

【讨论】:

  • 重复代码,例如两次指定相同的条件通常(总是?)是一个坏主意,并且知道 ruby​​ 代码的作用,但是如果第一个 xyz 出现在之前,sed 代码将什么也不打印并退出输入中的第一个abc
  • @EdMorton 否。顺序指令是任何程序的基本构建块。虽然某些语言特性(例如 sed 保持空间、Perl/Ruby 触发器运算符或支持的正则表达式引擎中的多行匹配)可以说让您避免需要单独的模式匹配而不是回溯,但您不是在我目前能想到的任何方法中,真正避免第二条指令。如果您想证明我错了,请提供您自己的答案以供考虑。
  • idk 你在说我的评论的哪一部分No,但我会做什么:因为他很亲密,我刚刚评论了@RavinderSingh13's answer,所以他在他的建议中包含了我的建议,但它是只是awk '/abc/{f=1} f{print; if (/xyz/) exit}'
  • @EdMorton 计算您的条件。您只是在为变量测试交易模式匹配。您现在拥有三个命令/语句,而不是两个。
  • 我不关心代码中有多少条件,因为这完全不相关。除了为发布的示例输入和合理可预测的其他输入提供预期输出之外,我还关心没有重复的代码(所以当我的开始或结束正则表达式发生变化时,我不必更改代码中的 2 个位置)以及在哪里对条件进行代码测试(所以我只在必要时执行测试)并在打印第一个/唯一可能的匹配块后立即退出。
【解决方案5】:

这已经被比我更好的头脑很好地充分地回答和讨论了,但是

  1. 因为您明确使用了sed,并且
  2. 对于处理请求条件的各种方法...
sed -n '/abc/,/xyz/{ p; /xyz/q; }' filename
  • 这只会查看有问题的范围,因此不会在 xyz 上打印或退出,而前面没有打开 abc
  • 打印范围内的所有记录
  • 退出它在abc之后看到的第一个xyz,所以会可靠地退出
  • 如果结尾没有 xyz,它只会打印到 EOF。

如果您想确保完全匹配,您可以优化模式,例如

sed -n '/^abc$/,/^xyz$/{ p; /^xyz$/q; }' filename

这可以防止近似匹配混淆逻辑,但(故意)不原谅拙劣的哨兵字符串。

【讨论】:

  • 对于非 GNU sed,用换行符替换分号。
【解决方案6】:

这可能对你有用(GNU sed):

 sed '/abc/!d;:a;n;/xyz/!ba;q' file

如果不是包含abc的行,删除它。

否则,打印并获取下一个。

如果该行不是xyz,请重复。

否则,退出。

注意未设置选项-n,因此将在终止前打印最后一行。

这将打印到文件末尾或遇到字符串xyz

如果xyz 必须存在,请使用:

sed -n '/abc/!d;:a;N;/xyz/!ba;p;q' file

【讨论】:

    猜你喜欢
    • 2020-10-15
    • 2012-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-28
    • 2019-10-26
    • 1970-01-01
    相关资源
    最近更新 更多