如何找到行索引然后在 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
你好,我有一个简单的问题,我需要在 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