【问题标题】:How to grep lines non-repeatedly for same command?如何为同一命令不重复地 grep 行?
【发布时间】:2019-05-23 23:12:16
【问题描述】:

我有一个空格分隔的文件,如下所示:

$ cat in_file
GCF_000046845.1_ASM4684v1_protein.faa WP_004920342.1 Chal_sti_synt_C
GCF_000046845.1_ASM4684v1_protein.faa WP_004927566.1 Chal_sti_synt_C
GCF_000046845.1_ASM4684v1_protein.faa WP_004919950.1 FAD_binding_3
GCF_000046845.1_ASM4684v1_protein.faa WP_004920342.1 FAD_binding_3

我正在使用以下 shell 脚本,利用 grep 来搜索字符串:

$ cat search_script.sh
grep "GCF_000046845.1_ASM4684v1_protein.faa WP_004920342.1" Pfam_anntn_temp.txt
grep "GCF_000046845.1_ASM4684v1_protein.faa WP_004920342.1" Pfam_anntn_temp.txt

问题是我希望每个 grep 命令只返回它找到的字符串的第一个实例exclusive 之前相同的 grep 命令的输出。

我需要一个如下所示的输出:

$ cat out_file
GCF_000046845.1_ASM4684v1_protein.faa WP_004920342.1 Chal_sti_synt_C
GCF_000046845.1_ASM4684v1_protein.faa WP_004920342.1 FAD_binding_3

其中第 1 行是第一个 grep 命令的输出,第 2 行是第二个 grep 命令的输出。我该怎么做?

附:我在一个大文件(>125,000 行)上运行它。所以,search_script.sh 主要由独特的 grep 命令组成。正是相同命令的执行打乱了我的下游分析。

【问题讨论】:

  • @WiktorStribiżew 我已经回答了这个问题及其答案。我的问题不同,该问题的答案不符合我的目的。
  • 我撤销了接近投票。
  • 我认为您不需要重复该命令。使用一次 (it seems to extract what you need),获取所有匹配项,然后您可以对其进行迭代。
  • 正如@WiktorStribiżew 建议的那样,只运行一次grep 并将结果放入一个数组中,然后依次使用它们循环遍历数组...参见此处的第2 点并将您的grep代替my_commandstackoverflow.com/a/32931403/2836621

标签: grep


【解决方案1】:

我假设您是从in_file 的内容自动生成search_script.sh。如果你能数出你会重复多少次相同的 grep 命令,你可以只使用一次 grep 并使用 head,例如,如果你知道你将使用它 2 次:

grep "foo" bar.txt | head -2

将输出 bar.txt 中前 2 次出现的“foo”。

如果您必须单独执行 grep 命令,例如如果您在 grep 命令之间有其他代码,您可以混合使用 headtail

grep "foo" bar.txt | head -1 | tail -1

Some other commands...

grep "foo" bar.txt | head -2 | tail -1
  • head -n 显示输入的第一行 n
  • tail -n 显示输入的最后一行 n

如果您确实必须始终使用相同的命令,但确保输出始终不同,我能想到的唯一方法是使用临时文件和复杂的命令序列:

 cat foo.bar.txt.tmp 2>&1 | xargs -I xx echo "| grep -v \\'xx\\' " | tr '\n' ' '  | xargs -I xx sh -c "grep 'foo' bar.txt xx | head -1 | tee -a foo.bar.txt.tmp"

所以为了解释这个命令,给定foo作为搜索字符串和bar.txt作为文件名,那么foo.bar.txt.tmp是临时文件的唯一名称。临时文件将保存已经输出的字符串:

  • cat foo.bar.txt.tmp 2>&1 :输出临时文件的内容。如果不存在,将向标准输出输出一条错误消息,(很重要,因为如果输出为空,则命令的其余部分将不起作用。)
  • xargs -I xx echo "| grep -v \\'xx\\' "| grep -v 添加到临时文件中每一行的开头,grep -v something 不包括包含something 的行。
  • tr '\n' ' ' 用空格替换换行符,在单个字符串上包含 grep -vs 序列。
  • xargs -I xx sh -c "grep 'foo' bar.txt xx | head -1 | tee -a foo.bar.txt.tmp" 运行一个新命令 grep 'foo' bar.txt xx | head -1 | tee -a foo.bar.txt.tmp,将 xx 替换为之前的输出。 xx 应该是排除先前输出的 grep -vs 的序列。
  • head -1 确保一次只输出一行
  • tee -a foo.bar.txt.tmp 将新输出附加到临时文件中。

请务必清除脚本末尾的临时文件 rm *.tmp

【讨论】:

    【解决方案2】:

    如果我的问题是正确的,并且您想根据每行的最后一个字段删除重复项,请尝试关注(这对awk 来说应该很容易)。

    awk '!a[$NF]++'  Input_file
    

    【讨论】:

      猜你喜欢
      • 2021-07-04
      • 2022-12-12
      • 1970-01-01
      • 2012-07-31
      • 2018-05-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-19
      相关资源
      最近更新 更多