Bash 一个文件中两个模式之间的sed/awk:模式1由来自第二个文件行的变量设置;由指定字符指定的模式2

Bash 一个文件中两个模式之间的sed/awk:模式1由来自第二个文件行的变量设置;由指定字符指定的模式2,bash,awk,sed,grep,Bash,Awk,Sed,Grep,我有两个文件。一个文件包含我想在第二个文件中匹配的模式。我想使用该模式在包含的模式之间打印,最多打印一个未包含的指定字符,然后连接到单个输出文件中 比如说, 文件1: a c d 和文件2: >a MEEL >b MLPK >c MEHL >d MLWL >e MTNH 我一直在使用这个循环的变体: while read $id; do sed -n "/>$id/,/>/{//!p;}" File_2;

我有两个文件。一个文件包含我想在第二个文件中匹配的模式。我想使用该模式在包含的模式之间打印,最多打印一个未包含的指定字符,然后连接到单个输出文件中

比如说,

文件1:

a
c
d
和文件2:

>a
MEEL
>b
MLPK
>c
MEHL
>d
MLWL
>e
MTNH
我一直在使用这个循环的变体:

while read $id;
     do 
       sed -n "/>$id/,/>/{//!p;}" File_2;
done < File_1
但我没有这样的运气。我曾与grep/fgrep-awk和sed打过交道,这三者之间似乎无法获得正确或任何输出。有人能给我指出正确的方向吗?

试试:

$ awk -F'>' 'FNR==NR{a[$1]; next}  NF==2{f=$2 in a} f'  file1 file2
>a
MEEL
>c
MEHL
>d
MLWL
工作原理 -F'>'

这会将字段分隔符设置为>

FNR==NR{a[$1];next}

在读取第一个文件时,这会为文件中的每一行在数组中创建一个键

NF==2{f=$2在a}

对于文件2中有两个字段的每一行,如果第二个字段是a中的键,则将变量f设置为true;如果不是,则将变量f设置为false

f

如果f为真,则打印该行

尝试:

工作原理 -F'>'

这会将字段分隔符设置为>

FNR==NR{a[$1];next}

在读取第一个文件时,这会为文件中的每一行在数组中创建一个键

NF==2{f=$2在a}

对于文件2中有两个字段的每一行,如果第二个字段是a中的键,则将变量f设置为true;如果不是,则将变量f设置为false

f

如果f为真,则打印该行


让我试着解释一下为什么你的方法效果不佳:

您需要说while read id,而不是while read$id。 sed命令/>$id/,/>/{/!p;}将排除起始行 使用>。 然后你可能想说:

while read id; do
    sed -n "/^>$id/{N;p}" File_2
done < File_1
但是上面的代码效率很低,因为它读取文件_2的次数是文件_1中id计数的次数。
请尝试John1024提供的优雅解决方案。

让我试着解释一下为什么您的方法效果不佳:

您需要说while read id,而不是while read$id。 sed命令/>$id/,/>/{/!p;}将排除起始行 使用>。 然后你可能想说:

while read id; do
    sed -n "/^>$id/{N;p}" File_2
done < File_1
但是上面的代码效率很低,因为它读取文件_2的次数是文件_1中id计数的次数。 请尝试由John1024提供的优雅解决方案。

如果ed可用,并且由于涉及外壳

!/usr/bin/env bash mapfile-t to_match 由于mapfile,需要bash4+

工作原理

mapfile-t to_match ed-s file2.txt将ed指向带有-s标志的file2,这意味着不打印有关该文件的信息,与您在wc文件中获得的信息相同

^是一个表示开始的锚

[]是一个括号表达式,它匹配其中的任何内容,在本例中是数组${to_match[*]}的值

);包括下一个地址/模式

/^>/匹配一个前导>

-我在模式匹配后返回一行

p打印与图案匹配的任何内容

q退出ed

如果ed可用,并且由于涉及壳牌公司

!/usr/bin/env bash mapfile-t to_match 由于mapfile,需要bash4+

工作原理

mapfile-t to_match ed-s file2.txt将ed指向带有-s标志的file2,这意味着不打印有关该文件的信息,与您在wc文件中获得的信息相同

^是一个表示开始的锚

[]是一个括号表达式,它匹配其中的任何内容,在本例中是数组${to_match[*]}的值

);包括下一个地址/模式

/^>/匹配一个前导>

-我在模式匹配后返回一行

p打印与图案匹配的任何内容

q退出ed

一个简单的GNU-sed解决方案。文件只读取一次。假设sed表达式中不需要引用文件_1中的字符

pat=$(sed ':a; $!{N;ba;}; y/\n/|/' File_1)
sed -E -n ":a; /^>($pat)/{:b; p; n; /^>/ba; bb}" File_2
说明: 对sed的第一次调用生成一个正则表达式,用于对sed的第二次调用,并将其存储在变量pat中。目的是避免对文件_1的每一行重复读取整个文件_2。它只是对文件_1发出咕噜声,并用|个字符替换新行字符。因此示例文件_1变成一个值为a | c | d的字符串。正则表达式a | c | d匹配,如果此示例中的备选方案a、b、c中至少有一个匹配,则这是GNU-sed扩展

第二个sed表达式:a;/^>$pat/{:b;p;n;/^>/ba;bb},可以 将转换为如下所示的伪代码:

begin:
    read next line (from File_2) or quit on end-of-file
label_a:
    if line begins with `>` followed by one of the alternatives in `pat` then
label_b:
        print the line
        read next line (from File_2) or quit on end-of-file
        if line begins with `>` goto label_a else goto label_b
    else goto begin
一个简单的GNU-sed解决方案。文件只读取一次。假设sed表达式中不需要引用文件_1中的字符

pat=$(sed ':a; $!{N;ba;}; y/\n/|/' File_1)
sed -E -n ":a; /^>($pat)/{:b; p; n; /^>/ba; bb}" File_2
说明: 对sed的第一次调用生成一个正则表达式,用于对sed的第二次调用,并将其存储在变量pat中。目的是避免对文件_1的每一行重复读取整个文件_2。它只是对文件_1发出咕噜声,并用|个字符替换新行字符。因此示例文件_1变成一个值为a | c | d的字符串。正则表达式a | c | d匹配,如果此示例中的备选方案a、b、c中至少有一个匹配,则这是GNU-sed扩展

第二个sed表达式:a;/^>$pat/{:b;p;n;/^>/ba;bb},可以转换为如下伪代码:

begin:
    read next line (from File_2) or quit on end-of-file
label_a:
    if line begins with `>` followed by one of the alternatives in `pat` then
label_b:
        print the line
        read next line (from File_2) or quit on end-of-file
        if line begins with `>` goto label_a else goto label_b
    else goto begin

在while-read循环中运行sed基本上总是错误的。@tripleee-true。我只是猜猜OP想说什么。我不打算在循环中使用sed。在while read循环中运行sed基本上总是错误的。@tripleee true。我只是猜猜OP想说什么。我不打算在循环中使用sed。您似乎正在处理一个文件,对吗?如果是,请添加标签。您似乎正在处理一个文件,对吗?如果是这样,请添加标签。这是我建议的解决方案,虽然我实际上更喜欢Awk,但您可能应该更详细地解释如何使用sed为。。。塞德。ed的解决方案大致相似,但可以说更为深奥。主要的一点是,给定一个好的脚本,sed只需要处理一次主输入文件。这避免了烦人的“while read”循环,该循环需要从开始到结束读取输入文件的次数与输入模式的次数一样多。将模式存储在一个只使用一次的变量中是一个模糊的问题,特别是当您有很多模式时。@tripleee我同意awk解决方案更好、更优雅。我将对我的回答作一个解释。第一行中的命令替换可以嵌入到第二行中的sed表达式中,但随后就很难遵循这一长行了。在某些平台上,您可以执行sed操作| sed-f-以标准输入将生成的脚本传递给第二个sed实例。在这种情况下,这将需要一些重构,一些sed实现根本不接受stdin上的脚本。@tripleee在bash中,也可以使用进程替换。例如,这适用于bash和GNU sed:sed-E-n-f这是我建议的解决方案,虽然我实际上更喜欢Awk,但您可能应该更详细地解释如何使用sed为。。。塞德。ed的解决方案大致相似,但可以说更为深奥。主要的一点是,给定一个好的脚本,sed只需要处理一次主输入文件。这避免了烦人的“while read”循环,该循环需要从开始到结束读取输入文件的次数与输入模式的次数一样多。将模式存储在一个只使用一次的变量中是一个模糊的问题,特别是当您有很多模式时。@tripleee我同意awk解决方案更好、更优雅。我将对我的回答作一个解释。第一行中的命令替换可以嵌入到第二行中的sed表达式中,但随后就很难遵循这一长行了。在某些平台上,您可以执行sed操作| sed-f-以标准输入将生成的脚本传递给第二个sed实例。在这种情况下,这将需要一些重构,一些sed实现根本不接受stdin上的脚本。@tripleee在bash中,也可以使用进程替换。例如,这适用于bash和GNU-sed:sed-E-n-f这假设every>指示头的开始,并且从不在内部出现。如果主文件实际上是一个FASTA文件,我相信这是一个有效的假设。使用GNU Awk,您可以将记录分隔符设置为类似^>的正则表达式模式。f是一种切换,在匹配键的以下数据行上仍然为true,在不匹配键的以下数据行上仍然为false-是吗?我只是想说,这是美丽的,如果迟钝的话。XDThis假设every>指示标头的开始,并且从不在行内部出现。如果主文件实际上是一个FASTA文件,我相信这是一个有效的假设。使用GNU Awk,您可以将记录分隔符设置为类似^>的正则表达式模式。f是一种切换,在匹配键的以下数据行上仍然为true,在不匹配键的以下数据行上仍然为false-是吗?我只是想说,这是美丽的,如果迟钝的话。除息的