공통 문자열을 표시하는 연속된 두 줄의 위치를 ​​바꿉니다.

공통 문자열을 표시하는 연속된 두 줄의 위치를 ​​바꿉니다.

대략적인 내용을 포함하는 텍스트 파일이 있습니다. 1,200만 행, 각 행은 4개의 필드(1, 2, 3, 4열)로 구성됩니다.

대부분의 행에는 열 2에 고유한 STRING이 있습니다. 나는 이 줄을 수정하고 싶지 않습니다.

텍스트 파일에는 열 2에 동일한 STRING이 포함된 2개의 연속 행이 있는 경우가 많습니다. 이는 텍스트 파일 전체에서 약 10,000번 발생합니다. 예가 아래에 나와 있습니다.

column1 column2 column3 column4  
WT 1 ILS G  
WT 2 DSG E          
WT 3 WYT S 
. . . .  
WT 106  AAA X  
WT 106  BBB Y  
. . . .  
WT 2704 CCC X  
WT 2704 DDD Y   
. . . .  

내가 달성하고 싶은 것:

column1 column2 column3 column4  
WT 1 ILS G  
WT 2 DSG E          
WT 3 WYT S 
. . . .  
WT 106  BBB Y    
WT 106  AAA X  
. . . .   
WT 2704 DDD Y   
WT 2704 CCC X  
. . . .   

나에게는 어떤 자원이 있나요?

10,000개의 문자열이 포함된 텍스트 파일이 있습니다. 이 문자열은 두 번 발생하며(예제에서는 106 및 2704) 두 줄을 바꿔야 합니다. 또한 동일한 열 2가 있는 행에서는 X와 Y가 항상 동일하다는 것도 알고 있습니다.

나는 지금까지 무엇을 했는가?

나는 공통 문자열(예: 106으로 지정)이 있는 두 줄을 식별하고 sed를 사용하여 바꾸는 방법을 알고 있습니다. 동적으로 만드는 방법(식별하기 위해 10000개의 문자열이 있는 텍스트 파일 사용)을 모르므로 10000개의 명령을 개별적으로 실행할 필요가 없습니다.

귀하의 시간과 도움에 미리 감사드립니다. 최고,

로랑

답변1

awk의 무차별 대입 방법: 항상 행을 저장하고 필드 1에서 일치하는 후속 행을 승격합니다.

백만 개의 행(스왑 하나 사용)에서 테스트했는데 5.5초 만에 실행되었으므로 런타임은 1분 남짓이 될 것입니다. 참조 파일이 필요하지 않습니다.

테스트 라인이 포함된 HereDoc을 포함한 스크립트입니다.

#! /bin/bash

awkPairs () {

    local Awk='
FNR == 1 { k = $2; x = $0; next; } 
$2 != k { print x; k = $2; x = $0; next; } 
{ print $0; }
END { print x; }
'
    awk -f <( printf '%s' "${Awk}" ) -
}

    [ x ] && time awkPairs <<'[][]'
WT 1 One x1
WT 2 Two x2
WT 3 Three_1 x3
WT 3 Three_2 y3
WT 4 Four x4 
WT 5 Five_1 x5
WT 5 Five_2 y5
[][]

(단기) 테스트 실행.

$ ./awkPairs
WT 1 One x1
WT 2 Two x2
WT 3 Three_2 y3
WT 3 Three_1 x3
WT 4 Four x4
WT 5 Five_2 y5
WT 5 Five_1 x5

real    0m0.009s
user    0m0.004s
sys     0m0.006s

모든 테스트 자료 스크립트를 제거했습니다. 단일 인수(입력 파일 이름) 또는 리디렉션되거나 파이프되는 표준 입력을 사용하여 호출할 수 있습니다. 출력은 항상 stdout으로 이동합니다.

#! /bin/bash

awkPairs () {

    local Awk='
FNR == 1 { k = $2; x = $0; next; }
$2 != k { print x; k = $2; x = $0; next; }
{ print $0; }
END { print x; }
'
    awk -f <( printf '%s' "${Awk}" ) "${1:--}"
}

    awkPairs "${1}"

따라서 다음 방법 중 하나로 호출할 수 있습니다.

./awkPairs myData.txt
./awkPairs < myData.txt
cat myData.txt | ./awkPairs

답변2

GNU sed는 확장 정규식 모드에 있습니다 -E(정규식을 덜 시끄럽게 만듭니다). 패턴 공간에 두 줄을 저장하고 두 줄의 첫 번째 필드를 비교합니다. 일치하면 패턴 공간에서 교체된 줄을 인쇄하고 다음 줄을 읽습니다.

sed -Ee '
  $!N
  s/^(\S+\s+(\S+)\s.*)\n(\S+\s+\2\s.*)/\3\n\1/
  t;P;D
' file

참고: 이는 "파일"에 선행 공백이 없다고 가정합니다.

답변3

가정만오른쪽바꿔야 하는 줄 수(즉, 동일한 두 번째 필드에 3개 이상의 연속 줄이 아님), 파일에 최소한 두 줄이 포함되어 있습니다.

function possibly_swap() {
        if (stringa == stringb) {
                # The two previous lines needs swapping.
                t = linea
                linea = lineb
                lineb = t
        }
}

FNR >= 3 {
        possibly_swap()

        # Output the 2nd previous line (possibly swapped now).
        print lineb
}

{
        # Push new data.

        stringb = stringa
        lineb = linea

        stringa = $2
        linea = $0
}

END {
        # We may need to output the last two
        # lines swapped...
        possibly_swap()

        print lineb
        print linea
}

프로그램 은 두 개의 awk변수 세트, stringastringb를 사용합니다 . 변수에는 입력 라인의 가장 최근 두 라인 중 두 번째 필드인 문자열이 포함됩니다. 변수 에는 해당하는 내용이 포함되어 있습니다.linealinebstringline가득한철사.

코드 전체에 사용된 접미사 합계는 이전 줄과 그 앞 줄("앞의 두 번째 줄")에 a해당합니다 .b

위 코드를 자체 파일(여기서는 )에 넣고 아래와 같이 다른 파일(여기서는 ) script.awk에서 실행할 수 있습니다.file

awk -f script.awk file

"one-liner"와 동일한 코드:

awk 'FNR>=3{if(sa==sb){t=la;la=lb;lb=t}print lb}{sb=sa;lb=la;sa=$2;la=$0}END{if(sa==sb){print la;print lb}else{print lb;print la}}' file

답변4

awk '
f {
    swap = $2 == p2
    print (swap ? $0 : prev)
}

!swap {
    prev = $0
    p2 = $2
    f = 1
}

END { if (f) print prev }' file

1개 레코드의 인쇄를 지연합니다. 현재 두 번째 필드가 이전 필드와 동일한지 여부에 따라 인쇄할 레코드(현재 또는 이전)를 선택합니다. 현재 레코드가 인쇄되면 prev업데이트되지 않습니다. 동일한 두 번째 필드가 있는 연속 레코드는 사실상 한 단계 위로 이동하며 첫 번째 레코드는 마지막 위치로 이동됩니다. 마지막으로 나머지 레코드를 인쇄합니다(입력에 존재하는 경우).

이 "전송"을 최대 연속 레코드 수(예: 스왑 쌍만)로 제한하려면 swap = $2 == p2로 변경하면 됩니다 swap = $2 == p2 && f++ < 2.

관련 정보