使用文本文件每一行的修改版本作为 sed 命令中的参数 (bash)

use a modified version of each line of a text file as argument in a sed command (bash)

我需要从 file1 中提取两个字符串之间的所有文本。第一个字符串是 file2 的每一行,第二个字符串始终是“Lambda”。但是,我不知道如何在我的 sed 命令中说明 file2 的每个字符串。此外,我需要在 file2 的每一行的开头删除一个“>”,以匹配 file1 的内容:

示例文件 1:

some_text1

random text

Lambda

some_text2

random text

Lambda

some_text3

random text

Lambda

示例文件 2:

>some_text1
>some_text3

我想出了这个不完整的 1 行命令:

sed -n '/**line from file2, without ">" at the beginning**/,/^Lambda/p' file1

虽然不完整,但这是我的循环想法(这不包括删除 >,我在命令中也需要它):

for line in file1; do sed -n '/$line/,/^Lambda/p' file1; done

示例输出(请注意 some_text2 不存在,因为它不在文件 2 上:

some_text1

random text

Lambda
some_text3

random text

Lambda

我能做什么?

运行 的多个副本 sed 因为这是非常低效的。下面是一个awk脚本,只需要读取一次file1,不管file2有多长:

#!/usr/bin/env bash
awk '
  BEGIN   { in_block=0 }
  NR==FNR { array[substr([=10=], 2)]=1; next }
  in_block == 0 {
    for (item in array) {
      if ([=10=] ~ item) {
        in_block=1
        print([=10=])
        next
      }
    }
  }
  in_block == 1 { print }
  in_block == 1 && /^Lambda/ { in_block=0 }
' file2 file1

您可以使用 sed 通过创建一个匹配 file2 中所有字符串的单一模式,然后 运行 它只在 file1 上一次来更有效地做到这一点。对于您的示例,模式类似于 (some_text1|some_text3) (尽管这是“扩展”正则表达式语法,因此您需要使用 sed -E )。像这样:

lines=$(sed -n 's/^>//p' file2)    # This just reads in the lines with > removed
pattern="(${lines//$'\n'/|})"      # This actually converts them to a regex pattern
sed -En "/${pattern}/,/^Lambda/ p" file1    # Extract all matching ranges

请注意,如果您希望 file2 中的字符串匹配整行,而不仅仅是行中的某处,您可以使用:

pattern="^(${lines//$'\n'/|})$"    # The ^ and $ anchor to the beginning & end of line

此外,请注意,如果 file2 中的行包含任何正则表达式元字符,它们将被视为它们的正则表达式含义;如果您希望将它们视为严格的文字字符串,则需要对它们进行预处理以转义 shell 元字符。如果它们包含 /,那也需要转义。

在循环中使用 sed 主要是不好的做法。您可以考虑使用下面的版本,它首先创建 sed 命令(使用 sed 本身!),然后调用 sed 来处理这些命令:

 sed -n -f <(sed           \
     -e 's/.//'            \
     -e 's/[]\/*.[]/\&/g' \
     -e 's%.*%/^&$/,/^Lambda$/p%' file2) file1

如果可以保证 file2 不包含任何 []\/*. 个字符,您可能希望省略 -e 's/[]\/*.[]/\&/g' 部分。注意<(...)表达式是process substitution in bash;它显示为包含括号之间命令输出的文件。

尝试

{mawk/mawk2/gawk} 'BEGIN { FS = "^[>]"; FN = ARGV[--ARGC];     
        
        while (getline < FN) { lookupL[]++ }; 
        close(FN);

        FN = ARGV[ARGC] = ""; 
        FS = "^Lambda";
    } { 
        match([=10=], /[[:graph:]]+/); 

       if (substr([=10=], RSTART, RLENGTH) in lookupL) { 

           do { print; 
                if (NF>1) {break} 

           } while (getline); 
       }
   }' file1 file2

file2 的大小应该不是什么大问题,除非你说的超过 2 GB。