【问题标题】:Grep N times from pipe using xargs使用 xargs 从管道中 Grep N 次
【发布时间】:2016-04-21 19:13:02
【问题描述】:

我有一个名为 input 的文件,其中包含维基百科列表或维基百科标题的子字符串。我只想打印出维基百科标题的行,而不是子字符串。

我有另一个名为 wikititle 的文件,其中包含所有 wikipedia 标题的列表。所以我想从输入中提取每一行,如果它与 ^{string}$ 匹配,我想打印出该行。

我想出了以下命令:

cat input | xargs -0 -I{} bash -c 'grep -q -w ^{}$ wikititle && { echo {}; }'

但它给了我一个错误:

 xargs: command too long

我该如何做到这一点?谢谢!

【问题讨论】:

  • 顺便说一句——“命令太长”的最可能原因是使用 -0 而您的输入 实际上 没有以 NUL 分隔。如果您的输入实际上是换行符分隔的并且您使用了xargs -0,那么它将尝试创建一个命令,同时将文件中的每个文件名都替换到其中(包括换行符分隔符)。
  • 您是否尝试过将-l1(小写L,然后是1)传递给xargs?如果你的输入是一个常规的文本文件,你不想使用-0,这通常意味着从find -print0 提供。
  • ...另外,顺便说一句,为什么grep -q ... && echo?离开-q 离开grep 不会有同样的效果吗?如果您想确保不超过一个匹配项,-m 1 会这样做。
  • 另外,^${foo}$ 并不能完全保证与${foo} 匹配。想想如果foo 包含方括号、问号或任何其他有趣的正则表达式字符会发生什么。
  • @jamieguinan,我认为遇到 NUL 分隔流的地方比单独的 find -print0 多得多;以/proc/*/environ 为例。当需要将参数列表序列化到文件(或同样用于数组)时,我也是printf '%s\0' "$@" 的习惯用户。但是,是的,同意 OP 在这里可能没有 NUL 分隔的流。 :)

标签: bash unix awk grep xargs


【解决方案1】:

打印两个文件中的行的正确方法是使用comm

comm -12 <(sort input) <(sort wikititle)

大大比您尝试执行的操作更有效率:它只运行一次,一次只需要在内存中存储很少的内容(sort 可以有更大的内存要求,但 GNU 实现支持使用磁盘支持的临时存储)。


另一种更有效的方法如下:

grep -F -x -f input wikititle

...这将运行grep 只运行一次,使用input 中给出的所有(换行符分隔的)字符串,与wikititle 的内容相对应。

使用grep -F 避免将参数视为正则表达式,因此即使像Foo [Bar] 这样的字符串在完全锚定时也会匹配自己(它们不会与将[Bar] 视为字符类的grep 匹配)。使用-x 需要全行匹配(谢谢@tripleee!)。


...而且,如果您真的想要使用 xargs 和一大堆单独的 grep 调用和一个 shell 级别的 echo 没有充分的理由...

<input xargs bash -c \
  'for line; do grep -q -F -x -e "$line" wikititle && printf '%s\n' "$line"; done' _

请注意,这不使用-I '{}',这是一个使xargs 效率低得多的选项(强制它为每个匹配运行一次命令),并且在与@ 一起使用时还会引入潜在的安全错误987654338@(如果您的输入文件中的一行包含$(rm -rf ~),您可能不想执行它)。相反,它在您的 bash 中使用 for 循环来迭代作为参数传递的文件名。

【讨论】:

  • 使用grep -x,您根本不需要 awk 预处理。
  • @tripleee,太好了,谢谢!这让我可以更改为grep -F,从而避免了先前方法中隐含的正确性问题。
【解决方案2】:

如果没有样本输入和预期输出,这只是猜测,但听起来您只需要:

awk 'NR==FNR{titles[$0];next} $0 in titles' wikititle input

请记住,shell 是一个用来操作文件和进程以及调用工具的环境,而不是一个操作文本的工具。创建 shell 的人还创建了 awk 供 shell 调用以操作文本。

【讨论】:

  • 嗯。创建 ksh 的人,从 POSIX sh 规范的早期版本中获得灵感,非常希望它具有足够的性能和足够的能力来独立进行计算(包括文本处理)。历史权威在这里并不是明确一致的。 :)
  • ...虽然我们都完全同意 shell 外部的工具对于这个特定任务来说是正确的,但我不知道为什么我要费心去争论所使用的论点。我认为comm 通常是比awk 更好的工具用于此任务的原因是内存使用——需要将两个文件的内容之一加载到哈希表中可能可行,也可能不可行。
  • 我并不是要建议您不能在 shell 中进行文本处理。显然你可以做到,这只是一个非常糟糕的主意,因为默认情况下 shell 所做的并不是你在处理文本时想要做的(例如,在读取它们时扩展转义字符并从行的前面/后面去除空白)等等获得正确的代码非常困难,并且与 awk 相比,最终结果总是非常慢。 comm 和几个 sorts 和一些 bash 进程替换对于这个任务来说很好,但是如果不是通过将文件加载到内存中,comm 是如何工作的?
  • 我不会说输入IFS= read -r line“非常”困难。至于“总是非常慢”,对于 bash 或 dash 以及大多数第 3 方 ksh 克隆都是如此,但不是真正的 David Korn ksh93。
  • 有一些区别(不,实际上没有存储两个sorts的结果),这是我在聊天中做出的。不过,我们同意我们在细节上争论不休,并在大范围内达成一致。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-12-11
  • 2014-07-19
  • 2019-02-18
  • 2016-06-05
  • 1970-01-01
  • 2020-07-17
  • 1970-01-01
相关资源
最近更新 更多