SED 특정 열 교체

SED 특정 열 교체

Sed 명령을 사용하여 열 4에서 A를 RA로 바꾸려면 어떻게 해야 합니까? (이 열 사이에 여러 개의 공백이 포함되어 있습니다.)

ATOM     32  P     A     2       6.882  -5.338   6.560  1.00  0.00           P  
ATOM     33  OP1   A     2       7.505  -5.970   7.750  1.00  0.00           O  
ATOM     34  OP2   A     2       5.404  -5.201   6.610  1.00  0.00           O  
TER

합격할 수 있다awk '{gsub("A","RA",$4)}1' a.txt > b.txt

답변1

간격이 고정되어 있어야 하므로 이는 전체 레이아웃이 고정되어 있음을 의미할 수 있으며, ERE를 활성화하려면 GNU sed 또는 OSX/BSD sed와 같은 -E를 지원하는 sed를 사용하십시오.

$ sed -E 's/(.{17})A /\1RA/' file
ATOM     32  P     RA    2       6.882  -5.338   6.560  1.00  0.00           P
ATOM     33  OP1   RA    2       7.505  -5.970   7.750  1.00  0.00           O
ATOM     34  OP2   RA    2       5.404  -5.201   6.610  1.00  0.00           O
TER

또는 POSIX sed를 사용하십시오.

$ sed 's/\(.\{17\}\)A /\1RA/' file
ATOM     32  P     RA    2       6.882  -5.338   6.560  1.00  0.00           P
ATOM     33  OP1   RA    2       7.505  -5.970   7.750  1.00  0.00           O
ATOM     34  OP2   RA    2       5.404  -5.201   6.610  1.00  0.00           O
TER

편집: 입력 내용이 일치하지 않는 것 같습니다.PDB 표준@bushman이 이전에 게시했지만 그런 경우 다음을 사용하여 f[]레이블/이름으로 필드 배열을 만들고 레이블/이름으로 수정하고(입력의 상대 위치가 아닌) 인쇄를 사용할 수 있습니다. 동일한 고정 너비 형식:

$ cat tst.awk
BEGIN {
    # Record Format (copied from http://www.wwpdb.org/documentation/file-format-content/format33/sect9.html#ATOM)
    #
    #                 COLUMNS        DATA  TYPE    FIELD        DEFINITION
    #                 -------------------------------------------------------------------------------------
    flds[++numFlds]="  1 -  6        Record name   ATOM  "
    flds[++numFlds]="  7 - 11        Integer       serial       Atom  serial number."
    flds[++numFlds]=" 13 - 16        Atom          name         Atom name."
    flds[++numFlds]=" 17             Character     altLoc       Alternate location indicator."
    flds[++numFlds]=" 18 - 20        Residue name  resName      Residue name."
    flds[++numFlds]=" 22             Character     chainID      Chain identifier."
    flds[++numFlds]=" 23 - 26        Integer       resSeq       Residue sequence number."
    flds[++numFlds]=" 27             AChar         iCode        Code for insertion of residues."
    flds[++numFlds]=" 31 - 38        Real(8.3)     x            Orthogonal coordinates for X in Angstroms."
    flds[++numFlds]=" 39 - 46        Real(8.3)     y            Orthogonal coordinates for Y in Angstroms."
    flds[++numFlds]=" 47 - 54        Real(8.3)     z            Orthogonal coordinates for Z in Angstroms."
    flds[++numFlds]=" 55 - 60        Real(6.2)     occupancy    Occupancy."
    flds[++numFlds]=" 61 - 66        Real(6.2)     tempFactor   Temperature  factor."
    flds[++numFlds]=" 77 - 78        LString(2)    element      Element symbol, right-justified."
    flds[++numFlds]=" 79 - 80        LString(2)    charge       Charge  on the atom."

    for (fldNr=1; fldNr<=numFlds; fldNr++) {
        fld = flds[fldNr]

        cols = substr(fld,1,16)
        gsub(/ /,"",cols)
        n = split(cols,begEnd,/-/)

        tag  = substr(fld,31,13)
        gsub(/ /,"",tag)

        tags[fldNr] = tag
        begs[tag] = begEnd[1]
        wids[tag] = begEnd[n] - begEnd[1] + 1

        # Uncomment this if interested in the values the arrays contain:
        # print "<" fldNr "><" tags[fldNr] "><" begs[tag] "><" wids[tag] ">" | "cat>&2"
    }
}

{
    for (fldNr=1; fldNr<=numFlds; fldNr++) {
        tag = tags[fldNr]
        f[tag] = substr($0,begs[tag],wids[tag])
        gsub(/^ +| +$/,"",f[tag])
    }
}

f["resName"] == "A" { f["resName"] = "RA" }     # this is where you can change a field by its tag/name

{
    for (fldNr=1; fldNr<=numFlds; fldNr++) {
        tag = tags[fldNr]
        printf "%-*s", wids[tag], f[tag]
    }
    print ""
}
$
$ awk -f tst.awk file
ATOM  32   P    RA  2    6.882   -5.338  6.560   1.00  0.00  P
ATOM  33   OP1  RA  2    7.505   -5.970  7.750   1.00  0.00  O
ATOM  34   OP2  RA  2    5.404   -5.201  6.610   1.00  0.00  O
TER

분명히 이것은 지금 하려는 작업에 비해 약간 과잉이지만 명심해야 할 좋은 일반적인 접근 방식이며 관심 있는 분야에 대해 이전에 논의한 다른 접근 방식에서 발생할 수 있는 문제를 해결합니다. .

답변2

사용sed

sed -E "s/^(([^ ]+ +){3})A  /\1RA /" file1

송곳

입력 라인에서 시작^

([^ ] +)( )공백이 아닌 문자의 연속된 시퀀스와 [^ ]+그 뒤에 또 다른 연속된 공백 문자 시퀀스 로 집합을 캡처하는 것을 나타냅니다.+

이 그룹의 반복을 잡아서 {3}다음 문자가 네 번째 필드에 나타나도록 하세요.

세 개의 반복을 모두 래핑하여 그룹화합니다.( )

지금 저장한 "수퍼그룹" \1뒤에 A(예: A에 2개의 공백이 있음) 다음으로 바꾸면 \1RA(즉, 후행 공백이 1개이므로 문자 수를 동일하게 유지함)

답변3

열 대체를 사용하여 보기 좋게 인쇄된 테이블 출력을 생성하려면 명령에 다음을 추가하면 awk됩니다.

awk ' gsub("A","RA",$4){for (i=1;i<=NF;i++){printf("%-9s",$i)}print "" }' a.txt > b.txt

출력은 cat b.txt다음과 같습니다.

ATOM     32       P        RA       2        6.882    -5.338   6.560    1.00     0.00     P
ATOM     33       OP1      RA       2        7.505    -5.970   7.750    1.00     0.00     O
ATOM     34       OP2      RA       2        5.404    -5.201   6.610    1.00     0.00     O

각 열 사이에 더 구체적인 간격을 찾고 있다면 루프를 포기 printf하고 각 필드에 개별 간격을 할당해야 합니다.


매우 빠른 대안을 찾고 있다면 sed언제든지 다음과 같이 할 수 있습니다.

sed 's/ A /RA/' a.txt > b.txt

sed하지만 이렇게 하면 다른 필드 분할 방법이 사용되므로 네 번째 열뿐만 아니라 파일의 모든 "A"가 "RA"로 대체됩니다 awk. 그럼에도 불구하고 위 sed명령을 사용하면 cat b.txt다음과 같은 결과가 나타납니다.

ATOM     32  P    RA    2       6.882  -5.338   6.560  1.00  0.00           P
ATOM     33  OP1  RA    2       7.505  -5.970   7.750  1.00  0.00           O
ATOM     34  OP2  RA    2       5.404  -5.201   6.610  1.00  0.00           O

답변4

방법 1: 역참조 없는 GNU sed

$ sed -re '
     h
     s/\S+/\n&\n/4
     s/\nA\n/RA/;t
     g
 ' file

방법 2: 역참조가 포함된 GNU sed.

 $ sed -re '
      s/^(\s*(\S+\s+){3})(A(\s|$))/\1R\3/
 ' file

두 sed 솔루션 모두 Posix와 호환되지만 코드 명확성이 희생되고 백슬래시염이 발생할 수 있습니다.

방법 3: Perl은 awk와 유사한 필드를 사용하지만 한 가지 차이점은 이 필드가 필드 값과 구분 기호를 모두 저장한다는 것입니다. 따라서 4번째 필드는 8번째 필드가 되며, 0 인덱스 배열을 사용하는 경우 7이 됩니다.

$ perl -F'/(\S+)/' -lane '
     $F[7] =~ s/^A$/RA/;
     print @F;\
 ' file 

관련 정보