用sed或awk删除行

时间:2012-12-11 12:35:59

标签: sed awk

我有一个像这样的文件data.txt。

>1BN5.txt
207
208
211
>1B24.txt
88
92

我有一个包含文本文件的文件夹F1。

F1文件夹中的1BN5.txt文件如下所示。

ATOM    421  CA  SER A 207      68.627 -29.819   8.533  1.00 50.79           C 
ATOM    421  CA  SER A 207      68.627 -29.819   8.533  1.00 50.79           C  
ATOM    422  C   SER A 248      70.124 -29.955   8.226  1.00 55.81           C 
ATOM    615  H   LEU B 208       3.361  -5.394  -6.021  1.00 10.00           H
ATOM    616  HA  LEU B 211       2.930  -4.494  -3.302  1.00 10.00           H 
ATOM    626  N   MET B  87       1.054  -3.071  -5.633  1.00 10.00           N  
ATOM    627  CA  MET B  87      -0.213  -2.354  -5.826  1.00 10.00           C 

F1文件夹中的1B24.txt文件如下所示。

ATOM    630  CB  MET B  87      -0.476  -2.140  -7.318  1.00 10.00           C 
ATOM    631  CG  MET B  88      -0.828  -0.688  -7.575  1.00 10.00           C
ATOM    632  SD  MET B  88      -2.380  -0.156  -6.830  1.00 10.00           S
ATOM    643  N   ALA B  92      -1.541  -4.371  -5.366  1.00 10.00           N  
ATOM    644  CA  ALA B  94      -2.560  -5.149  -4.675  1.00 10.00           C

我只需要在1BN5.txt文件中包含207,208,211(第6列)的行。我想删除1BN5.txt文件中的其他行。像这样,我只需要在1B24.txt文件中包含88,92的行。

Desired output

1BN5.txt文件

ATOM    421  CA  SER A 207      68.627 -29.819   8.533  1.00 50.79           C
ATOM    421  CA  SER A 207      68.627 -29.819   8.533  1.00 50.79           C 
ATOM    615  H   LEU B 208       3.361  -5.394  -6.021  1.00 10.00           H  
ATOM    616  HA  LEU B 211       2.930  -4.494  -3.302  1.00 10.00           H

1B24.txt文件

ATOM    631  CG  MET B  88      -0.828  -0.688  -7.575  1.00 10.00           C
ATOM    632  SD  MET B  88      -2.380  -0.156  -6.830  1.00 10.00           S
ATOM    643  N   ALA B  92      -1.541  -4.371  -5.366  1.00 10.00           N 

7 个答案:

答案 0 :(得分:5)

这是使用GNU awk的一种方式。像:

一样运行
awk -f script.awk data.txt

script.awk的内容:

/^>/ {
    file = substr($1,2)
    next
}

{
    a[file][$1]
}

END {

    for (i in a) {

        while ( ( getline line < ("./F1/" i) ) > 0 ) {

            split(line,b)

            for (j in a[i]) {

                if (b[6]==j) {

                    print line > "./F1/" i ".new"
                }
            }
        }

        system(sprintf("mv ./F1/%s.new ./F1/%s", i, i))
    }
}

或者,这是单行:

awk '/^>/ { file = substr($1,2); next } { a[file][$1] } END { for (i in a) { while ( ( getline line < ("./F1/" i) ) > 0 ) { split(line,b); for (j in a[i]) if (b[6]==j) print line > "./F1/" i ".new" } system(sprintf("mv ./F1/%s.new ./F1/%s", i, i)) } }' data.txt


如果您的awk旧版本早于GNU Awk 4.0.0,则可以尝试以下操作。像:

一样运行
awk -f script.awk data.txt

script.awk的内容:

/^>/ {
    file = substr($1,2)
    next
}

{
    a[file]=( a[file] ? a[file] SUBSEP : "") $1
}

END {

    for (i in a) {

        split(a[i],b,SUBSEP)

        while ( ( getline line < ("./F1/" i) ) > 0 ) {

            split(line,c)

            for (j in b) {

                if (c[6]==b[j]) {

                    print line > "./F1/" i ".new"
                }
            }
        }

        system(sprintf("mv ./F1/%s.new ./F1/%s", i, i))
    }
}

或者,这是单行:

awk '/^>/ { file = substr($1,2); next } { a[file]=( a[file] ? a[file] SUBSEP : "") $1 } END { for (i in a) { split(a[i],b,SUBSEP); while ( ( getline line < ("./F1/" i) ) > 0 ) { split(line,c); for (j in b) if (c[6]==b[j]) print line > "./F1/" i ".new" } system(sprintf("mv ./F1/%s.new ./F1/%s", i, i)) } }' data.txt


请注意,此脚本完全按照您的描述执行。它希望1BN5.txt1B24.txt等文件驻留在当前工作目录的文件夹F1中。它还会覆盖原始文件。如果这不是所需的行为,请放弃system()电话。 HTH。

结果:

F1/1BN5.txt的内容:

ATOM    421  CA  SER A 207      68.627 -29.819   8.533  1.00 50.79           C 
ATOM    421  CA  SER A 207      68.627 -29.819   8.533  1.00 50.79           C  
ATOM    615  H   LEU B 208       3.361  -5.394  -6.021  1.00 10.00           H
ATOM    616  HA  LEU B 211       2.930  -4.494  -3.302  1.00 10.00           H 

F1/1B24.txt的内容:

ATOM    631  CG  MET B  88      -0.828  -0.688  -7.575  1.00 10.00           C
ATOM    632  SD  MET B  88      -2.380  -0.156  -6.830  1.00 10.00           S
ATOM    643  N   ALA B  92      -1.541  -4.371  -5.366  1.00 10.00           N

答案 1 :(得分:1)

不要尝试从现有文件中删除行,尝试创建一个只包含您想要的行的新文件:

cat 1bn5.txt | awk '$6 == 207 || $6 == 208 || $6 == 211 { print }' > output.txt

答案 2 :(得分:1)

假设gnu awk,从包含data.txt

的目录运行此命令
awk -F">" '{if($2 != ""){fname=$2}if($2 == ""){term=$1;system("grep "term" F1/"fname" >>F1/"fname"_results");}}' data.txt

这会解析data.txt的文件名和搜索字词,然后从grep内部调用awk,将data.txt中列出的每个文件和字词的匹配项追加到新文件中在名为F1的{​​{1}}中。

如果要完全替换原始文件,则可以运行以下命令:

originalfilename.txt_results

答案 3 :(得分:1)

此解决方案在记录分隔符中使用了一些技巧:“data.txt”使用&gt; 作为记录分隔符,而其他文件使用换行符。

awk '
    BEGIN {RS=">"}
    FNR == 1 {
        # since the first char in data.txt is the record separator, 
        # there is an empty record before the real data starts
        next
    }
    {
        n = split($0, a, "\n")
        file = "F1/" a[1]
        newfile = file ".new"
        RS="\n"
        while (getline < file) {
            for (i=2; i<n; i++) {
                if ($6 == a[i]) {
                    print > newfile
                    break
                }
            }
        }
        RS=">"
        system(sprintf("mv \"%s\" \"%s.bak\" && mv \"%s\" \"%s\"", file, file, newfile, file))
    }
' data.txt 

答案 4 :(得分:1)

这会将F1中的所有文件移动到名为“backup”的tmp目录,然后在F1下重新创建非空文件

mv F1 backup &&
mkdir F1 &&
awk '
NF==FNR {
   if (sub(/>/,"")) {
      file=$0
      ARGV[ARGC++] = "backup/" file
   }
   else {
      tgt[file,$0] = "F1/" file
   }
   next
}
(FILENAME,$6) in tgt {
   print > tgt[FILENAME,$6]
}
' data.txt &&
rm -rf backup

如果你也想要空文件,这是一个微不足道的调整,如果你想保留备份目录,最后摆脱"&& rm.."(无论如何都要做到这一点)。

编辑:FYI这是一个案例,你可以认为getline不是完全错误的情况,因为它解析的第一个文件完全不同于结构和意图中的其他文件,所以解析一个文件不同于其余文件以后不会引起任何维护问题:

mv F1 backup &&
mkdir F1 &&
awk -v data="data.txt" '
BEGIN {
   while ( (getline line < data) > 0 ) {
      if (sub(/>/,"",line)) {
         file=line
         ARGV[ARGC++] = "backup/" file
      }
      else {
         tgt[file,line] = "F1/" file
      }
   }
}
(FILENAME,$6) in tgt {
   print > tgt[FILENAME,$6]
}
' &&
rm -rf backup

但正如你所看到的那样,它使脚本变得更复杂(虽然效率稍高,因为现在主体中没有对FNR == NR的测试)。

答案 5 :(得分:0)

绝对是awk的工作:

$ awk '$6==207||$6==208||$6==211 { print }' 1bn5.txt
ATOM    421  CA  SER A 207      68.627 -29.819   8.533  1.00 50.79           C 
ATOM    421  CA  SER A 207      68.627 -29.819   8.533  1.00 50.79           C  
ATOM    615  H   LEU B 208       3.361  -5.394  -6.021  1.00 10.00           H
ATOM    616  HA  LEU B 211       2.930  -4.494  -3.302  1.00 10.00           H 

$ awk '$6==92||$6==88 { print }' 1B24.txt
ATOM    631  CG  MET B  88      -0.828  -0.688  -7.575  1.00 10.00           C
ATOM    632  SD  MET B  88      -2.380  -0.156  -6.830  1.00 10.00           S
ATOM    643  N   ALA B  92      -1.541  -4.371  -5.366  1.00 10.00           N 

重定向以保存输出:

$ awk '$6==207||$6==208||$6==211 { print }' 1bn5.txt > output.txt

答案 6 :(得分:0)

我认为只用sed就可以做到这一点。您需要一个循环来读取您的文件data.txt。例如,使用bash脚本:

#!/bin/bash

# First remove all possible "problematic" characters from data.txt, storing result
# in data.clean.txt. This removes everything except A-Z, a-z, 0-9, leading >, and ..
sed 's/[^A-Za-z0-9>\.]//g;s/\(.\)>/\1/g;/^$/d' data.txt >| data.clean.txt

# Next determine which lines to keep:
cat data.clean.txt | while read line; do
   if [[ "${line:0:1}" == ">" ]]; then
      # If input starts with ">", set remainder to be the current file
      file="${line:1}"
   else
      # If value is in sixth column, add "keep" to end of line
      # Columns assumed separated by one or more spaces
      # "+" is a GNU extension, so we need the -r switch
      sed -i -r "/^[^ ]+ +[^ ]+ +[^ ]+ +[^ ]+ +$line +/s/$/keep/" $file
   fi
done

# Finally delete the unwanted lines, i.e. those without "keep":
# (assumes each file appears only once in data.txt)
cat data.clean.txt | while read line; do
   if [[ "${line:0:1}" == ">" ]]; then
      sed -i -n "/keep/{s/keep//g;p;}" ${line:1}
   fi
done