【问题标题】:How to grep from a file and save the results as separate files如何从文件中 grep 并将结果另存为单独的文件
【发布时间】:2019-08-27 12:35:39
【问题描述】:

我正在尝试 grep 几种模式,这些模式已存储在一个文本文件中。 我想在另一个文件中 grep 这些模式中的每一个,并将每个 grep 的结果保存在一个单独的文本文件中。

简化示例:

数据(存储在targets.txt):

ERR751956   1.1
ERR718556   1.1
ERR775770   1.1
ERR553258   1.1.1
SRR5067466  1.1.1
ERR768007   1.1.1
ERR2515049  1.2.1
ERR2517293  1.2.1
ERR400534   1.2.1
ERR1034610  1.2.2
ERR221606   1.2.2
SRR2101271  1.2.2
ERR718276   2.1
SRR5709934  2.1
SRR5065676  2.2
SRR5709903  2.2

我正在尝试根据第一个小数位对数据进行分组,因此我搜索了这些模式,并将其存储在一个名为 patterns.txt 的文件中:

"\s1.1
\s1.2
\s2.1
\s2.2"

当我单独运行 grep 时,它可以工作,例如grep "\s1.1" targets.txt

我想要做的是 grep 所有这些模式并将结果存储为一个单独的文件,最好使用 target_1.1.txt 格式。

我尝试了许多循环的变体,包括这个:

while IFS= read -r line 
do
    grep "$line" targets.txt > target_"$line".txt
done < patterns.txt

然而,

  1. 它保存了包含 grep 命令“\s”的文件名,我不想要,但是
  2. 它不会提取所有 grep 并将它们保存在一个文件中 - 有些结果只是一个空文件。

再一次,我已经运行了它没有单独拾取的那些,它们打印到屏幕上就好了。

【问题讨论】:

  • 第一行和最后一行的引号是否真的在您的patterns.txt 文件中?我认为它们只是偶然出现在问题中,但如果它们实际上在文件中,您应该删除它们。

标签: bash for-loop grep


【解决方案1】:

我喜欢 @PaulHodges 使用 sed 的解决方案,但这里有一个替代解决方案,更接近您自己的原始脚本,以防您更喜欢使用这种类型的解决方案。

结果对所使用的模式非常具体,但我发现掌握 bash 中的变量替换很有用,所以我认为这是值得介绍的,尽管我更喜欢 Paul 的解决方案。

使用变量替换

在 bash 中,有很多方法可以使用变量替换来修改变量。您可以使用${var#prefix}$var 中删除前缀。在您的情况下,${line#\\s} 将仅包含版本号。

所以脚本可以变成:

while IFS= read -r line ; do
   out="target_${line#\\s}"
   grep "$line" targets.txt > "$out"
done < patterns.txt

转义点

现在,有一个潜在的问题:你没有逃避.,所以1.1 要求匹配1,然后是1。您可能想改用\s1\.1,在这种情况下脚本将再次中断。您可以使用 ${var//pattern/replacement} 来修复它,它会替换每次出现的模式:

while IFS= read -r line ; do
   out="target_${line#\\s}"
   out="${out//\\/}"
   grep "$line" targets.txt > "$out"
done < patterns.txt

更多信息

输入 man bash 并搜索 ## 以了解它支持的所有其他变量替换(#%/^, 及其双倍变体都很有趣)。

缺少输出:无法重现

由于某些输出文件为空,我无法重现该问题,因此无法在此处解决。如果引号确实在您的patterns.txt 文件中,那可能是问题所在,否则我不知道。

【讨论】:

    【解决方案2】:

    进程太多。试试sed。参考文献sed reference 用于使用w,但基本上它将(可能使用s/// 预先编辑的)行输出到命名文件。

    请注意,它会截断任何现有文件,但如您所见,随后对同一文件的写入会追加。

    所以对于你给定的数据 -

    $: sed -En '
        /\s1\.1/w target_1.1.txt
        /\s1\.2/w target_1.2.txt
        /\s2\.1/w target_2.1.txt
        /\s2\.2/w target_2.2.txt
    ' targets.txt
    
    $: # now let's look at the files -
       # grep prepends the filename & a colon
    
    $: grep . target*
    target_1.1.txt:ERR751956  1.1
    target_1.1.txt:ERR718556   1.1
    target_1.1.txt:ERR775770   1.1
    target_1.1.txt:ERR553258   1.1.1
    target_1.1.txt:SRR5067466  1.1.1
    target_1.1.txt:ERR768007   1.1.1
    target_1.2.txt:ERR2515049  1.2.1
    target_1.2.txt:ERR2517293  1.2.1
    target_1.2.txt:ERR400534   1.2.1
    target_1.2.txt:ERR1034610  1.2.2
    target_1.2.txt:ERR221606   1.2.2
    target_1.2.txt:SRR2101271  1.2.2
    target_2.1.txt:ERR718276   2.1
    target_2.1.txt:SRR5709934  2.1
    target_2.2.txt:SRR5065676  2.2
    target_2.2.txt:SRR5709903  2.2
    

    下面是一个简化的比较示例:

    $: cat in
    dog
    cat
    mouse
    flying fox
    hairless cat
    dingo
    panda
    
    $: # allocate lines to files
    
    $: sed '
    > /a/w a
    > /e/w e
    > /i/w i
    > /o/w o
    > /u/w u
    > ' in
    
    $: # now let's look at the files -
       # grep prepends the filename & a colon
    
    $: grep . a e i o u
    a:cat
    a:hairless cat
    a:panda
    e:mouse
    e:hairless cat
    i:flying fox
    i:hairless cat
    i:dingo
    o:dog
    o:mouse
    o:flying fox
    o:dingo
    u:mouse
    

    【讨论】:

    • 我喜欢你的回答,但是当我第一次看到你的简单示例时,我发现它令人困惑——在我理解之前,它似乎有很多令人困惑的东西。由于您的解决方案最后非常简洁,我建议从一小段开始解释w 的作用(可能只是引用您链接到的sed 手册),然后有你的简单示例,最后是 OP 的解决方案.或者,甚至,放弃简化的示例,直接使用 w 解释的 OP 解决方案。
    【解决方案3】:

    我认为你可以这样做:

    $ awk '{match($2,/[0-9]+[.][0-9]+/)}
           {print > ("target_" substr($2,RSTART,RLENGTH) ".txt") }' targets.txt
    

    【讨论】:

      【解决方案4】:

      在这个例子中,我更喜欢脚本中的模式。
      我将使用两行将模式更改为简单的字符串,并以sed 命令结束。

      while IFS= read -r line
      do
          # Remove qouble quotes (if any present)
          line=${line//\"}
          # Remove \s (sed command will add \s,)
          line=${line//\\s}
          # line is now like 1.2 or 2.1
      
          sed -n "/\s${line}/w target_${line}.txt" targets.txt
      done < patterns.txt
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-10-07
        • 1970-01-01
        • 1970-01-01
        • 2020-12-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多