如何找到行索引然后在 bash 中重写它

How to find line index then rewrite it in bash

你好,我有一个简单的问题,我需要在 txt 文件中找到特定的行,它们必须包含 'LG',看起来像这样:

>NC_037638.1 Apis mellifera strain DH4 linkage group LG1, Amel_HAv3.1, whole genome shotgun sequence

然后我需要用 LG1 替换这种情况下的数字 NC_037638.1 每行的 LG 和数字会有所不同

结果应如下所示:

>LG1, Apis mellifera strain DH4 linkage group LG1, Amel_HAv3.1, whole genome shotgun sequence

我在一个文件中有大约 300 万行,我只需要找到带有 LG 后跟一些数字的行,如示例 LG1

所以基本上我需要从中得到:

为此:

我写了这样的东西:

#!/bin/bash
while IFS= read -r line; do
    if [[ $line =~ "LG" ]]; then
        echo $line | awk ' { t = ;  = ; print; } '  | sed -e 's/^/>/' >> nowy.txt
    else
        echo $line >> nowy.txt
    fi
done < kopia_pliku_docelowego

它可以工作,但它的速度非常慢,脚本需要大约 3 分钟才能结束

我想到了解决方案,我想我可以 grep 获取行索引并仅更改那些行,然后在与新重写的索引相同的索引上交换旧行。

我知道如何找到索引(grep -n) 而且我知道如何更改线路(谈论与 LG 交换号码) 但我不知道如何把它们放在一起。

非常感谢您的帮助

您可以一次性完成此操作 sed:

sed -i.bak -E 's/^>NC_037638\.1(.* (LG[0-9]+))/>/' file

cat file

>LG1 Apis mellifera strain DH4 linkage group LG1, Amel_HAv3.1, whole genome shotgun sequence

解释:

  • ^>:在起始位置
  • 之后匹配>
  • NC_037638\.1:匹配文本NC_037638.1
  • (.* :Nn 捕获组 #1 匹配并捕获任何文本后跟 space 后跟...
  • (LG[0-9]+)):匹配 LG 后跟捕获组 #2
  • 中的 1+ 个数字
  • >:替换部分有 > 后跟 LG 子字符串(我们在组 #2 中捕获的内容)后跟捕获组 #1[=34 的反向引用=]

问题描述我不是很懂。听起来您只想将包含 LG 的任何行中的第一列替换为第 8 列。如果是这样,就这样做:

awk '/LG/{  =  }1' kopia_pliku_docelowego > nowy.txt

但也许您想限制匹配,以便仅在 'LG' 出现在第 8 列时才进行替换。你可以这样做:

awk ' ~ /LG/{  =  }1'

如果您要求 LG 后跟一串数字,请使用:

awk ' ~ /LG[0-9]+/{  =  }1'

如果您有第 8 列为 LGxxxAAA 的行(数字后面的非字符串值)并且您只想用匹配 LG[0-9+] 的字符串部分替换第一列,您可以使用:

awk 'match(,/LG[0-9]+/){  = substr(,0,RLENGTH) }1'

awk无疑可以解决你的问题,但你需要明确你要匹配的是什么。您的 sed 解决方案似乎插入了前导 >,根据您的描述,这似乎没有必要。需要更具体。

只是 awk,也许:

awk '{
  for(i=1;i<NF-1;i++)
    if($i=="linkage" && $(i+1)=="group")
      break
  if(i!=NF-1)
    =$(i+2)
  print
}' file.txt

我们搜索两个连续的单词“linkage”和“group”,以防它们不总是相同行中的位置。我怀疑这可能是因为“Apis mellifera”看起来像是包含 space 的单个字段。如果我们找到这两个词,我们将第一个字段替换为“linkage group”之后的字段。

如果“链接组”后面的字段必须进一步约束,例如LGnnn 其中 nnn 是一些数字串,我们可以稍微改变一下条件:

awk '{
  for(i=1;i<NF-1;i++)
    if($i=="linkage" && $(i+1)=="group" && $(i+2) ~ /^LG[[:digit:]]+/)
      break;
  if(i!=NF-1)
    =$(i+2)
  print
}' file.txt