bash 循环替换某个字符后的字符串中间部分

bash 循环替换某个字符后的字符串中间部分

我有 120 个文件 (genomes.faa),每个文件之间都有标题

>GENOME1_00001 HYPOTHETICAL PROTEIN A
NQFTIAQSQVGLEDALLDL

>GENOME1_00002 HYPOTHETICAL PROTEIN B
NQFTIAQSQVGLEDALLDL

>GENOME1_00003 HYPOTHETICAL PROTEIN C
NQFTIAQSQVGLEDALLDL

etc.

我试图删除名称后面的“_0000X”并将其替换为“|”

>GENOME1|HYPOTHETICAL PROTEIN A
NQFTIAQSQVGLEDALLDL

>GENOME1|HYPOTHETICAL PROTEIN B
NQFTIAQSQVGLEDALLDL

>GENOME1|HYPOTHETICAL PROTEIN C
NQFTIAQSQVGLEDALLDL

etc.

我尝试这样做:

for file in *.faa
do
sed -r 's/_.*$/|/g' $file > $file.1
done

这不会在之后保留“假设蛋白质 A”,从而导致

>ERR1156171|
MMRQSVQTVLP 

代替

>ERR1156171|HYPOTHETICAL PROTEIN A
MMRQSVQTVLP 

任何帮助表示赞赏!

答案1

我认为你已经非常接近工作指挥了。这对我来说对你给出的几个例子很有用:

sed -E 's/_[0-9]+ /|/' "$file" > "$file.1"
  • 我将匹配表达式从 更改_.*_[0-9]+将匹配限制为仅下划线、数字和空格字符。
  • 我删除了$因为它匹配行的末尾,而不是第一个单词的末尾。
  • 我将替换命令的结尾从 更改/g为 ,/因为您的示例在每一行中只有一个位置需要编辑,而不是多个位置。
  • 另外,不要使用-E扩展-r正则表达式,因为前者与其他版本的 sed 更兼容;并引用变量扩展,以防任何文件名包含空格或特殊字符。

答案2

使用这个 Perl 单行命令:

perl -pe 's{^(>\S+?)(_\d+)?\s+(.*)}{$1|$3}' "$file" > "$file.1"

Perl 单行代码使用以下命令行标志:
-e:告诉 Perl 查找内联代码,而不是在文件中。
-p:一次循环输入一行,$_默认将其分配给。print $_在每次循环迭代后添加。

(...):捕获组,后面可以称为$1$2等。
\S+?:一个或多个非空白字符,非贪婪。
(_\d+)?:可选的匹配组,由下划线后跟 1 个或多个数字组成。
\s+: 1 个或多个空白字符。
(.*):任意字符,重复 0 次或多次。

也可以看看:
perldoc perlrun:如何执行Perl解释器:命令行开关
perldoc perlre:Perl 正则表达式(regexes)
perldoc perlre:Perl 正则表达式(regexes):量词;字符类和其他特殊转义;断言;捕获组
perldoc perlrequick:Perl正则表达式快速入门

答案3

这是一个简单的 Perl 单行代码,它将找到_以 a 开头的行中出现的第一个>,然后是一个或多个非空白字符 ( \S),并删除其后的所有非空白字符_以及其后的所有空白字符:

$ perl -pe 's/^(>\S+)_\S+\s*/$1|/' file
>GENOME1|HYPOTHETICAL PROTEIN A
NQFTIAQSQVGLEDALLDL

>GENOME1|HYPOTHETICAL PROTEIN B
NQFTIAQSQVGLEDALLDL

>GENOME1|HYPOTHETICAL PROTEIN C
NQFTIAQSQVGLEDALLDL

sed您也可以使用 GNU 做同样的基本事情:

$ sed -E 's/^(>\S+)_\S+\s*/\1|/' file
>GENOME1|HYPOTHETICAL PROTEIN A
NQFTIAQSQVGLEDALLDL

>GENOME1|HYPOTHETICAL PROTEIN B
NQFTIAQSQVGLEDALLDL

>GENOME1|HYPOTHETICAL PROTEIN C
NQFTIAQSQVGLEDALLDL

并与任何sed

$ sed 's/^\(>[^[:blank:]]*\)_[^[:blank:]]*[[:blank:]]*/\1\|/' file
>GENOME1|HYPOTHETICAL PROTEIN A
NQFTIAQSQVGLEDALLDL

>GENOME1|HYPOTHETICAL PROTEIN B
NQFTIAQSQVGLEDALLDL

>GENOME1|HYPOTHETICAL PROTEIN C
NQFTIAQSQVGLEDALLDL

答案4

我不知道为什么当你特别要求 bash 时每个人都给出其他语言的代码。

为此,使用 bash 的内置变量扩展工具,它比为每个文件名调用 sed 等外部​​程序要快得多。对于少数名称而言,这并不重要,但它可能会增加大量文件。

代码

#!/bin/bash

for file in "GENOME1_00001 HYPOTHETICAL PROTEIN A" "GENOME1_00002 HYPOTHETICAL PROTEIN B" "GENOME1_00003 HYPOTHETICAL PROTEIN C"
  do
     echo -n $file
     new_name="${file%_*}|HYPOTHETICAL PROTEIN ${file##*EIN }"
     echo " -> ${new_name}"
  done

不调用外部工具,产生输出

GENOME1_00001 HYPOTHETICAL PROTEIN A -> GENOME1|HYPOTHETICAL PROTEIN A
GENOME1_00002 HYPOTHETICAL PROTEIN B -> GENOME1|HYPOTHETICAL PROTEIN B
GENOME1_00003 HYPOTHETICAL PROTEIN C -> GENOME1|HYPOTHETICAL PROTEIN C

正如你所要求的。


正如评论中所解释的,我假设行开头的“>”是某种提示,并且只有这些行要被转换。恕我直言,修改代码以适应 Sotto Voce 的反对意见是相当简单的,但话又说回来,也许事实并非如此。按照 Sotto Voce 的要求,这是一个处理所有线路的版本。注意我已将输入数据转换为此处文档,并且像以前一样,为了提高效率,没有调用外部工具。

#!/bin/bash

while read line
  do
     if [ "${line%%GENOME1_*}" = ">" ]; then
          line="${line%_*}|HYPOTHETICAL PROTEIN ${line##*EIN }"
       fi
     echo "${line}"
  done << etc
>GENOME1_00001 HYPOTHETICAL PROTEIN A
NQFTIAQSQVGLEDALLDL

>GENOME1_00002 HYPOTHETICAL PROTEIN B
NQFTIAQSQVGLEDALLDL

>GENOME1_00003 HYPOTHETICAL PROTEIN C
NQFTIAQSQVGLEDALLDL

etc

这是输出:

>GENOME1|HYPOTHETICAL PROTEIN A
NQFTIAQSQVGLEDALLDL

>GENOME1|HYPOTHETICAL PROTEIN B
NQFTIAQSQVGLEDALLDL

>GENOME1|HYPOTHETICAL PROTEIN C
NQFTIAQSQVGLEDALLDL

相关内容