使用指南文件中的列替换多个匹配的字符串

使用指南文件中的列替换多个匹配的字符串

我有 2 个文件(FileA 和 FileB),

文件A:

s12 >g01
s16 >g02
s48 >g03
s52 >g04
s80 >g05
s81 >g06
s87 >g07
s91 >g08
s92 >g09
s93 >g10
s94 >g11
s96 >g12
s97 >g13
s98 >g14
s99 >g15
s100 >g16

文件B:

s12:1148.1652412 [PCC6803]
ABCDEFGHIJKLMNOPQRST
s16:1235.1653193 [PCC6803]
UVWXYZABCDEFGHIJKLMN
s48:5877.1652308 [PCC6803]
OPQRSTUVWXYZABCDEFGH
.
.
.

我想编辑 FileB,以便 FileB 中存在的所有“FileA 的第 1 列字符串”将更改为“FileA 的第 2 列字符串”

期望的输出:

>g01 [PCC6803]
ABCDEFGHIJKLMNOPQRST
>g02 [PCC6803]
UVWXYZABCDEFGHIJKLMN
>g03 [PCC6803]
OPQRSTUVWXYZABCDEFGH

我需要对大约 20 个与 FileB 格式相同的文件进行编辑。

有没有什么命令可以进行这种编辑?是同时执行还是在 Linux 终端中使用一行命令?提前致谢!

更新:我已经尝试过以下示例用不同的映射字符串集替换多个字符串 但它不起作用。

replacements=(
        s12:\>g01
        s16:\>g02
        s48:\>g03
        s52:\>g04
        s80:\>g05
        s81:\>g06
        s87:\>g07
        s91:\>g08
        s92:\>g09
        s93:\>g10
        s94:\>g11
        s96:\>g12
        s97:\>g13
        s98:\>g14
        s99:\>g15
        s100:\>g16
)

for row in "${replacement[@]}"; do
        original="$(echo $row | cut -d: -f1)";
        new="$(echo $row | cut -d: -f2)";
        sed -i -e "s/${original}/${new}/g" FileB;
done

答案1

$ awk 'FNR==NR { id[$1]=$2; next } { split($1,a,":"); if (a[1] in id) $1=id[a[1]]; print }' fileA fileB
>g01 [PCC6803]
ABCDEFGHIJKLMNOPQRST
>g02 [PCC6803]
UVWXYZABCDEFGHIJKLMN
>g03 [PCC6803]
OPQRSTUVWXYZABCDEFGH

第一个块仅在读取第一个文件 ( fileA) 时才会被触发。它将s*字符串到字符串的映射读取到以字符串作为键的>g*关联数组中。ids*

仅当读取第二个文件 ( ) 时才会触发第二个块fileB。它将把每行的第一个字段分割:成一个临时数组a。如果拆分结果的第一个元素是数组中的键id,则整个第一个字段将替换为该键的值。然后打印可能修改的行。

FNR是行号(实际上是记录号,但默认情况下记录是行)当前的文件,whileNR是总行号。因此,如果FNR==NR我们从第一个文件中读取。

答案2

一种方法是利用 的内容sed形成要对 的内容进行操作的命令。s///fileAfileB

$ sed -Ee 's/(.*) (>.*)/s|^\1:\\S+|\2|;t/' fileA | sed -Ef - fileB

输出:

>g01 [PCC6803]
ABCDEFGHIJKLMNOPQRST
>g02 [PCC6803]
UVWXYZABCDEFGHIJKLMN
>g03 [PCC6803]
OPQRSTUVWXYZABCDEFGH

解释:

让我们从反面看问题,即更改文件B。现在编辑 fileB 的第一行的 sed 命令会是什么样子?

  • 类似这样的事情: s/^s12:\S+/>g01/然后你就完成了这一行。因此,您标记一个空t行来告诉 sed 该行不需要进行更多编辑。
  • 其余行也类似。
  • 因此,我现在必须构建查看 fileA 的 sed 命令,您在其中指定了要执行的搜索 n 替换的所有可能映射。
  • 所需的任务是以某种方式将 fileA 转换为有效的 sed s/// 命令,这样当它们应用于 fileB 时,我们应该得到所需的结果。
  • 该任务由第一个 sed 命令执行:s/(.*) (>.*)/s|^\1:\\S+|\2|;t/
  • 第一部分:s/(.*) (>.*)/是 sed 替换命令的 lhs,是一个正则表达式,其中我们抓取并存储 fileA 任何给定行中的两个字段,例如,s12 >g01So \1Should stores12\2Should store >g01。当然,这里未提及的假设是,这些行恰好包含 2 个字段,其中有一个空格,没有前导空格,并且第二个字段以大于符号开头>
  • 因此 fileA 的行将根据 sed 命令的 rhss12 >g01进行转换。s|^s12:\S+|>g01|;t然后将转换后的行应用于 fileB,我们得到结果。
  • 为了便于理解,注释管道并查看第一个 sed 命令生成的内容,它就会开始变得清晰。 HTH。

答案3

你的sed命令几乎是正确的。您已经定义了一个名为 的数组replacements,但在for循环中,您使用了replacement。这就是它不起作用的原因。另外,您想要替换整行直到第一个空格,所以不仅仅是s/$original/$new/.这一个应该做你想做的事:

replacements=(
        s12:\>g01
        s16:\>g02
        s48:\>g03
        s52:\>g04
        s80:\>g05
        s81:\>g06
        s87:\>g07
        s91:\>g08
        s92:\>g09
        s93:\>g10
        s94:\>g11
        s96:\>g12
        s97:\>g13
        s98:\>g14
        s99:\>g15
        s100:\>g16
)

for row in "${replacements[@]}"; do
        original="$(echo $row | cut -d: -f1)";
        new="$(echo $row | cut -d: -f2)";
        sed -i -e "s/^${original}:[^ ]*/${new}/g" FileB;
done

现在这不是一种非常有效的方法,因为您需要为每次替换处理整个 fileB。更快的方法可能是:

$ awk 'NR==FNR{a[$1]=$2; next}{split($1, b, /:/); if(b[1] in a){$1=a[b[1]]}}1;' FileA FileB
>g01 [PCC6803]
ABCDEFGHIJKLMNOPQRST
>g02 [PCC6803]
UVWXYZABCDEFGHIJKLMN
>g03 [PCC6803]
OPQRSTUVWXYZABCDEFGH

并更改多个文件名:

awk 'NR==FNR{
        a[$1]=$2; 
        next
     }
     {
        split($1, b, /:/); 
        if(b[1] in a){
            $1=a[b[1]]
        }; 
        print > FILENAME".fixed"
    }' FileA FileB FileC FileD ... FileN

这将创建fileB.fixed,fileC.fixedfileD.fixed直到FileN.fixed。如果您对它的工作感到满意,您可以将它们重命名回原始文件名(假设您有 perl-rename,这是 Ubuntu 和 Debian 上的默认设置):

rename 's/fixed//' *fixed

或者,如果您没有perl-rename

for f in *fixed; do mv -- "$f" "${f%%.fixed}"; done

答案4

只需使用一次 GNU sed 调用即可完成此操作。您可以以 FileB 的格式提供任意数量的文件,而不是 FileB,但必须首先提供 FileA。为了安全起见,该命令将对输入文件进行备份。如果您对修改后的文件感到满意,则可以在此之后删除备份文件。

sed -ri.bk '1{x;s:^:cat /dev/fd/3:e;x};/:/{G;s/^([^:]+)\S+(\s+)([^\n]+).*\1\s+(>[^\n]+).*/\4\2\3/}' 3< FileA FileB

感谢@Stéphane Chazelas 给了我使用自定义文件描述符的想法,以解决使用 -i 时每个新文件上的保留空间被丢弃的问题。

相关内容