【问题标题】:Substitute several words in all files in a folder based on a .txt file基于 .txt 文件替换文件夹中所有文件中的几个单词
【发布时间】:2021-02-13 08:08:12
【问题描述】:

如果我有一个包含 4 列的 .txt 文件,如下所示:

File          Genus           Species            Strain
KPLB.S001.gbk Corynebacterium tuberculostearicum S001
KPLB.S098.gbk Corynebacterium propinquum         S098
KPLB.S045.gbk Corynebacterium tuberculostearicum S045
KPLB.S690.gbk Dolosigranulum  pigrum             S690

还有一个文件夹,其中包含第一列中列出的 4 个 .gbk 文件。如何根据表格将 .gbk 文件中出现的“Genus”、“Species”或“Strain”一词替换为相应的名称?

感谢任何指导。谢谢!

添加有关 .gbk 文件内部外观的信息:

LOCUS       NBOOEINI_1            375100 bp    DNA     linear       19-OCT-2020
DEFINITION  Genus species strain strain.
ACCESSION   
VERSION
KEYWORDS    .
SOURCE      Genus species
  ORGANISM  Genus species
            Unclassified.
COMMENT     Annotated using prokka 1.14.6 from
            https://github.com/tseemann/prokka.
FEATURES             Location/Qualifiers
     source          1..375100
                     /organism="Genus species"
                     /mol_type="genomic DNA"
                     /strain="strain"

这是想要的输出

LOCUS       NBOOEINI_1            375100 bp    DNA     linear       19-OCT-2020
DEFINITION  Corynebacterium tuberculostearicum strain S001.
ACCESSION   
VERSION
KEYWORDS    .
SOURCE      Corynebacterium tuberculostearicum
  ORGANISM  Corynebacterium tuberculostearicum
            Unclassified.
COMMENT     Annotated using prokka 1.14.6 from
            https://github.com/tseemann/prokka.
FEATURES             Location/Qualifiers
     source          1..375100
                     /organism="Corynebacterium tuberculostearicum"
                     /mol_type="genomic DNA"
                     /strain="S001"

strains 这个词会更复杂,因为我只需要更改 DEFINITION 行中的第二个匹配项以及最后一行

【问题讨论】:

  • 在我的脑海中,您可以使用lsfind 遍历文件,然后对于每个文件,然后使用cut 抓取字段,然后您可以生成一个将第 1 列中的值的所有实例替换为第 2 列中的值的单行代码。如果您发布 .gbk 文件的示例,我应该能够提供更好的帮助。
  • 感谢 ShawnMilo,我用 .gbk 文件的信息更新了问题
  • "那么您可以生成一个单行代码,将第 1 列中的值的所有实例替换为第 2 列中的值。"那是我比较难的部分

标签: macos sed


【解决方案1】:

好的,我用上面列出的模板内容创建了 4 个文件;我还创建了一个名为 control.txt 的文件,它与您上面的 .txt 文件匹配。

使用以下 shell 脚本作为 sed 的包装器可以满足您在安装时的要求。

$ cat replace.sh 
#!/bin/bash
tail -n +2 control.txt | while read file genus species strain
do
  sed -i "/^DEFINITION/s/Genus/$genus/;/^DEFINITION/s/species/$species/;/^DEFINITION/s/strain\./${strain}./;/^SOURCE/s/Genus/$genus/;/^SOURCE/s/species/$species/;/  ORGANISM/s/Genus/$genus/;/^  ORGANISM/s/species/$species/;/\/organism=/s/Genus/$genus/;/\/organism=/s/species/$species/;/\/strain=/s/\"strain\"/\"${strain}\"/" $file
done

【讨论】:

  • 谢谢!它给了我一个错误,因为我没有为执行 sed -i ' ' 的新文件命名。你的代码帮助我弄清楚了如何只在正确的地方改变“应变”
  • sed: 1: "NAME OF FILE ...": p 命令末尾的多余字符
  • 这很奇怪......您的实际文件名是否包含空格,或者您是否在 Windows 机器上创建了控制文本文件?
  • 我有一台 Mac。文件名如下:prokka_accolens_2617.spades.pilon.gbk
  • 显然 sed 需要一个输出文件,通过 sed -i "" 将输入文件作为输出写入。我就是这样解决的
【解决方案2】:

这可能不是最佳解决方案,但在我看来,它的可读性很强并且可能具有可扩展性:

CONFIG='config.txt'
DIRECTORY='/path/to/the/directory'

# These are the strings to be found in the files
SOURCE1='Genus'
SOURCE2='Species'
SOURCE3='Strain'

# Skip the first line with the titles of the columns
DATA=`tail -n +2 $CONFIG`

while IFS= read -r LINE
do
   # Get the columns of the config file and store them into separata variables
   FILENAME=`echo $LINE | awk '{print $1}'`
   REPLACE1=`echo $LINE | awk '{print $2}'`
   REPLACE2=`echo $LINE | awk '{print $3}'`
   REPLACE3=`echo $LINE | awk '{print $4}'`

   # Now replace each of the strings one by one, directly inside the file
   sed -i "s/$SOURCE1/$REPLACE1/g" $DIRECTORY/$FILENAME
   sed -i "s/$SOURCE2/$REPLACE2/g" $DIRECTORY/$FILENAME
   sed -i "s/$SOURCE3/$REPLACE3/g" $DIRECTORY/$FILENAME
done < <(printf '%s\n' "$DATA")

【讨论】:

  • 非常感谢。我主要基于您的答案中的最终代码。我修正了 Species and Strain 的拼写。我还修改了文本中“应变”出现的不同方式。另外我必须将 sed -i 编辑为 sed -i "" 因为给了我一个错误
【解决方案3】:

有点冗长,但是.... 需要具有 IGNORECASEgensub 支持的 gawk

$ cat tst.awk
FNR==NR {
   IGNORECASE=1
   if (FNR==1) {
       split($0,fldA)
       next
    }
    for(i=2;i<=NF;i++)
        arr[$1,fldA[i]]=$i
    fileA[$1]
    next
}
FILENAME in fileA {
    for(flds=2;flds in fldA;flds++)
       $0=gensub("([^/])" fldA[flds],"\\1" arr[FILENAME,fldA[flds]], "g")
}
1
$ cat KPLB.S001.gbk
OCUS       NBOOEINI_1            375100 bp    DNA     linear       19-OCT-2020
DEFINITION  Genus species strain strain.
ACCESSION
VERSION
KEYWORDS    .
SOURCE      Genus species
  ORGANISM  Genus species
            Unclassified.
COMMENT     Annotated using prokka 1.14.6 from
            https://github.com/tseemann/prokka.
FEATURES             Location/Qualifiers
     source          1..375100
                     /organism="Genus species"
                     /mol_type="genomic DNA"
                     /strain="strain"
$ cat tst.txt
File          Genus           Species            Strain
KPLB.S001.gbk Corynebacterium tuberculostearicum S001
KPLB.S098.gbk Corynebacterium propinquum         S098
KPLB.S045.gbk Corynebacterium tuberculostearicum S045
KPLB.S690.gbk Dolosigranulum  pigrum             S690

运行:gawk -f tst.awk tst.txt KPLB.S001.gbk 结果:

OCUS       NBOOEINI_1            375100 bp    DNA     linear       19-OCT-2020
DEFINITION  Corynebacterium tuberculostearicum S001 S001.
ACCESSION
VERSION
KEYWORDS    .
SOURCE      Corynebacterium tuberculostearicum
  ORGANISM  Corynebacterium tuberculostearicum
            Unclassified.
COMMENT     Annotated using prokka 1.14.6 from
            https://github.com/tseemann/prokka.
FEATURES             Location/Qualifiers
     source          1..375100
                     /organism="Corynebacterium tuberculostearicum"
                     /mol_type="genomic DNA"
                     /strain="S001"

添加一个 shell 包装器来处理多个文件和其他花里胡哨的东西留给 OP 作为练习。

【讨论】:

    【解决方案4】:

    这很棘手,因为不想替换文件中的 所有 个实例(特别是用“strain”,它被用作标签。这是一个可以工作的 Python 脚本。

    #!/usr/bin/env python3
    """
    Update .gbk files from config file.
    """
    
    import sys
    from os.path import exists
    
    
    # read config file for replacements
    def get_replacements():
        repls = []
        with open(sys.argv[1], 'r') as raw:
            for line in raw:
                # skip header row
                if line.startswith('File'):
                    continue
                repls.append(line.strip().split())
        return repls
    
    
    # replace last instance of 'old' in a line with 'new'
    def replace_last(line, old, new):
        if not old in line:
            return line
        i = line.rindex(old)
        return line[:i] + new + line[i+len(old):]
    
    def main():
        for filename, genus, species, strain in get_replacements():
            if not exists(filename):
                continue
            with open(filename, 'r') as raw:
                data = raw.read()
            update = []
            for line in data.split('\n'):
                line = replace_last(line, 'strain', strain)
                line = replace_last(line, 'Genus', genus)
                line = replace_last(line, 'species', species)
                update.append(line)
            with open(filename, 'w') as out:
                out.write('\n'.join(update))
    if __name__ == '__main__':
        main()
    

    请注意,此脚本会覆盖文件,从而丢失原始文件。我这样做是因为这似乎是你想做的。我在 Python 中添加了一些 cmets 以使其更易于理解,但我也尽量保持简短。

    【讨论】:

    • 非常感谢,我已经找到了另一段有用的代码并解决了这个问题。不过也谢谢你的回答
    【解决方案5】:

    谢谢大家。这就是最终对我有用的方法:

    CONFIG='Get_Homologues/Renamed_Prokka/control.txt'
    DIRECTORY='Get_Homologues/Renamed_Prokka/Genomes'
    
    # These are the strings to be found in the files
    SOURCE1='Genus'
    SOURCE2='species'
    SOURCE3='strain\.'
    SOURCE4='\"strain\"'
    
    # Skip the first line with the titles of the columns
    DATA=`tail -n +2 $CONFIG`
    
    while IFS= read -r LINE
    do
       # Get the columns of the config file and store them into separate variables
       FILENAME=`echo $LINE | awk '{print $1}'`
       REPLACE1=`echo $LINE | awk '{print $2}'`
       REPLACE2=`echo $LINE | awk '{print $3}'`
       REPLACE3=`echo $LINE | awk '{print $4}'`
    
       # Now replace each of the strings one by one, directly inside the file
       sed -i "" "s/$SOURCE1/$REPLACE1/g" $DIRECTORY/$FILENAME
       sed -i "" "s/$SOURCE2/$REPLACE2/g" $DIRECTORY/$FILENAME
       sed -i "" "s/$SOURCE3/$REPLACE3./g" $DIRECTORY/$FILENAME
       sed -i "" "s/$SOURCE4/\"$REPLACE3\"/g" $DIRECTORY/$FILENAME
    done < <(printf '%s\n' "$DATA")
    

    【讨论】:

    • 这是否适用于strain?例如,'/strain="strain"' 应该是 '/strain="S001"',而不是 '/S001="S001"。'
    • 是的。 SOURCE3='应变\.'仅替换句子中的第二个菌株:“定义属物种菌株菌株。”因为寻找点。并 SOURCE4='\"strain\"' 搜索“strain”。这样我得到 '/strain="S001"' 没有问题
    • 看来你在这里学到了一些好东西。您有权接受自己的答案并获得声誉积分(约 48 小时后),或者您可以接受其他人的答案并给予他们代表。您还可以对提供的任何/所有答案进行投票,以感谢作者花时间在您的问题上。 (您感谢所有贡献者也很高兴,这很少见!(-;)祝大家好运!
    • 是的@shellter,我学到了很多!我通常检查stackoverflow并将一些答案拼凑在一起,我通常会弄清楚如何做事。但昨天我终于放弃并创建了一个帐户来问我的第一个问题。因此,感谢您提供有关声誉点的信息,我并不真正了解该网站的运作方式。我很快就被许多问题弄得不知所措,其中一些问题在我处理第一个建议时出现了,所以我想感谢大家,即使我没有使用他们的代码。
    • 请注意,给予他人声望点数不会从您的声望点总数中扣除。我会鼓励这个问题的所有发帖者互相投票,因为他们都为你的问题提供了一些有价值的东西。祝大家好运。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-18
    • 1970-01-01
    • 1970-01-01
    • 2013-05-30
    • 1970-01-01
    • 2018-06-23
    相关资源
    最近更新 更多