가이드 파일의 열을 사용하여 일치하는 여러 문자열 바꾸기

가이드 파일의 열을 사용하여 일치하는 여러 문자열 바꾸기

2개의 파일(FileA 및 FileB)이 있습니다.

파일 A:

s12 >g01
s16 >g02
s48 >g03
s52 >g04
s80 >g05
s81 >g06
s87 >g07
s91 >g08
s92 >g09
s93 >g10
s94 >g11
s96 >g12
s97 >g13
s98 >g14
s99 >g15
s100 >g16

파일 B:

s12:1148.1652412 [PCC6803]
ABCDEFGHIJKLMNOPQRST
s16:1235.1653193 [PCC6803]
UVWXYZABCDEFGHIJKLMN
s48:5877.1652308 [PCC6803]
OPQRSTUVWXYZABCDEFGH
.
.
.

FileB에 있는 모든 "FileA의 열 1 문자열"이 "FileA의 열 2 문자열"로 변경되도록 FileB를 편집하고 싶습니다.

원하는 출력:

>g01 [PCC6803]
ABCDEFGHIJKLMNOPQRST
>g02 [PCC6803]
UVWXYZABCDEFGHIJKLMN
>g03 [PCC6803]
OPQRSTUVWXYZABCDEFGH

동일한 FileB 형식으로 약 20개의 파일을 편집해야 합니다.

이런 종류의 편집을 수행하는 명령이 있습니까? 동시에 실행됩니까, 아니면 Linux 터미널에서 한 줄의 명령을 사용하여 실행됩니까? 미리 감사드립니다!

업데이트: 다음 예를 시도했습니다.여러 문자열을 매핑된 문자열의 다른 집합으로 바꾸기 하지만 작동하지 않습니다.

replacements=(
        s12:\>g01
        s16:\>g02
        s48:\>g03
        s52:\>g04
        s80:\>g05
        s81:\>g06
        s87:\>g07
        s91:\>g08
        s92:\>g09
        s93:\>g10
        s94:\>g11
        s96:\>g12
        s97:\>g13
        s98:\>g14
        s99:\>g15
        s100:\>g16
)

for row in "${replacement[@]}"; do
        original="$(echo $row | cut -d: -f1)";
        new="$(echo $row | cut -d: -f2)";
        sed -i -e "s/${original}/${new}/g" FileB;
done

답변1

$ awk 'FNR==NR { id[$1]=$2; next } { split($1,a,":"); if (a[1] in id) $1=id[a[1]]; print }' fileA fileB
>g01 [PCC6803]
ABCDEFGHIJKLMNOPQRST
>g02 [PCC6803]
UVWXYZABCDEFGHIJKLMN
>g03 [PCC6803]
OPQRSTUVWXYZABCDEFGH

첫 번째 블록은 fileA첫 번째 파일( )을 읽을 때만 트리거됩니다. s*문자열을 키로 사용하여 문자열 간 매핑을 연관 배열로 읽 습니다 >g*.ids*

두 번째 블록은 두 번째 파일( )을 읽을 때만 트리거됩니다 fileB. 각 행의 첫 번째 필드를 :임시 배열로 분할합니다 a. 분할 결과의 첫 번째 요소가 배열의 키인 경우 id첫 번째 필드 전체가 해당 키의 값으로 대체됩니다. 그런 다음 수정되었을 수 있는 줄을 인쇄합니다.

FNR줄 번호입니다(실제로는 레코드 번호이지만 기본적으로 레코드는 줄입니다).현재의파일이고, 은 NR전체 줄 번호입니다. 따라서 FNR==NR첫 번째 파일에서 읽으면.

답변2

한 가지 방법은 콘텐츠를 사용하여 sed콘텐츠에 대한 작업 명령을 작성하는 것입니다.s///fileAfileB

$ sed -Ee 's/(.*) (>.*)/s|^\1:\\S+|\2|;t/' fileA | sed -Ef - fileB

산출:

>g01 [PCC6803]
ABCDEFGHIJKLMNOPQRST
>g02 [PCC6803]
UVWXYZABCDEFGHIJKLMN
>g03 [PCC6803]
OPQRSTUVWXYZABCDEFGH

설명하다:

파일 B를 변경하여 반대편에서 문제를 살펴보겠습니다. 이제 fileB의 첫 번째 줄을 편집하는 sed 명령은 어떤 모습일까요?

  • 다음과 같습니다. s/^s12:\S+/>g01/그러면 라인 작업이 완료됩니다. 따라서 빈 줄을 표시하여 t해당 줄에 더 이상 편집이 필요하지 않음을 sed에 알립니다.
  • 나머지 행은 유사합니다.
  • 따라서 이제 수행할 검색 n 교체의 가능한 모든 매핑을 지정하는 fileA를 살펴보는 sed 명령을 작성해야 합니다.
  • 필요한 작업은 fileA를 유효한 sed s/// 명령으로 변환하여 fileB에 적용할 때 원하는 결과를 얻는 것입니다.
  • 이 작업은 첫 번째 sed 명령으로 수행됩니다.s/(.*) (>.*)/s|^\1:\\S+|\2|;t/
  • 첫 번째 부분: s/(.*) (>.*)/sed 대체 명령의 lhs는 fileA의 특정 줄에 있는 두 필드를 가져와 저장하는 정규식입니다(예: s12 >g01So \1Should store s12\2Should store ) >g01. 물론 여기서 언급되지 않은 가정은 이 줄에 정확히 2개의 필드가 포함되어 있으며 그 중 하나에는 공백이 있고 선행 공백이 없고 두 번째 필드는 보다 큼 부호로 시작한다는 것입니다 >.
  • 따라서 fileA의 행은 s12 >g01sed 명령의 rhs에 따라 변환됩니다. s|^s12:\S+|>g01|;t변환된 줄은 fileB에 적용되고 결과를 얻습니다.
  • 더 쉽게 이해할 수 있도록 파이프라인을 주석 처리하고 첫 번째 sed 명령이 생성하는 내용을 살펴보면 명확해지기 시작할 것입니다. HTH.

답변3

귀하의 sed주문은 거의 정확합니다. 라는 배열을 정의했지만 루프 replacements에서는 을 사용합니다 . 그것이 작동하지 않는 이유입니다. 또한 첫 번째 공백까지 전체 줄을 바꾸고 싶으므로 이것이 원하는 작업을 수행해야 합니다.forreplacements/$original/$new/

replacements=(
        s12:\>g01
        s16:\>g02
        s48:\>g03
        s52:\>g04
        s80:\>g05
        s81:\>g06
        s87:\>g07
        s91:\>g08
        s92:\>g09
        s93:\>g10
        s94:\>g11
        s96:\>g12
        s97:\>g13
        s98:\>g14
        s99:\>g15
        s100:\>g16
)

for row in "${replacements[@]}"; do
        original="$(echo $row | cut -d: -f1)";
        new="$(echo $row | cut -d: -f2)";
        sed -i -e "s/^${original}:[^ ]*/${new}/g" FileB;
done

이제 교체할 때마다 전체 fileB를 처리해야 하므로 이는 매우 효율적인 접근 방식이 아닙니다. 더 빠른 방법은 다음과 같습니다.

$ awk 'NR==FNR{a[$1]=$2; next}{split($1, b, /:/); if(b[1] in a){$1=a[b[1]]}}1;' FileA FileB
>g01 [PCC6803]
ABCDEFGHIJKLMNOPQRST
>g02 [PCC6803]
UVWXYZABCDEFGHIJKLMN
>g03 [PCC6803]
OPQRSTUVWXYZABCDEFGH

그리고 여러 파일 이름을 변경하십시오.

awk 'NR==FNR{
        a[$1]=$2; 
        next
     }
     {
        split($1, b, /:/); 
        if(b[1] in a){
            $1=a[b[1]]
        }; 
        print > FILENAME".fixed"
    }' FileA FileB FileC FileD ... FileN

그러면 가 생성됩니다 fileB.fixed. 까지 fileC.fixed기다립니다 . 작동하는 것에 만족한다면 원래 파일 이름으로 다시 이름을 바꿀 수 있습니다(Ubuntu 및 Debian의 기본값인 perl-rename이 있다고 가정).fileD.fixedFileN.fixed

rename 's/fixed//' *fixed

또는 없는 경우 perl-rename:

for f in *fixed; do mv -- "$f" "${f%%.fixed}"; done

답변4

단 한 번의 GNU sed 호출로 이 작업을 수행할 수 있습니다. FileB 대신 FileB 형식으로 파일을 원하는 수만큼 제공할 수 있지만 FileA를 먼저 제공해야 합니다. 안전상의 이유로 이 명령은 입력 파일의 백업을 만듭니다. 수정된 파일이 만족스러우면 이후 백업 파일을 삭제해도 됩니다.

sed -ri.bk '1{x;s:^:cat /dev/fd/3:e;x};/:/{G;s/^([^:]+)\S+(\s+)([^\n]+).*\1\s+(>[^\n]+).*/\4\2\3/}' 3< FileA FileB

-i를 사용할 때 새 파일마다 예약된 공간이 삭제되는 문제를 해결하기 위해 사용자 정의 파일 설명자를 사용하는 아이디어를 제공한 @Stéphane Chazelas에게 감사드립니다.

관련 정보