共通文字列を表示する2つの連続した行の位置を置き換えます。

共通文字列を表示する2つの連続した行の位置を置き換えます。

おおよその内容を含むテキストファイルがあります。 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個の文字列を含むテキストファイルがあります。この文字列は2回(例では106と2704)で、2行を置き換える必要があります。また、同じ列2の行では、XとYが常に同じであることもわかっています。

私は今まで何をしましたか?

私は共通の文字列(例えば106で指定)を持つ2行を識別し、sedを使用して置き換える方法を知っています。動的にする方法(識別するために10000個の文字列を含むテキストファイルを使用)がわからないため、10000個のコマンドを個別に実行する必要はありません。

あなたの時間と助けに感謝します。最高、

ローラン

答え1

awkの無差別代入方法:常に行を保存し、フィールド1で一致する後続の行を昇格します。

100万行(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(正規表現をあまり騒々しくしません)。パターン空間に2行を格納し、2行の最初のフィールドを比較します。一致する場合は、パターン空間で置き換えられた行を印刷し、次の行を読みます。

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

注:これは「ファイル」に先行スペースがないと想定しています。

答え3

家だけ置き換える必要がある行の数(つまり、同じ2番目のフィールドに3つ以上の連続行ではない)、ファイルには少なくとも2行が含まれています。

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
}

プログラムは2つのawk変数セットをstringa使用stringbします。変数には、入力行の最後の2行の2番目のフィールドである文字列が含まれます。変数には対応する内容が含まれています。linealinebstringlineいっぱいワイヤー。

コード全体で使用されるサフィックスの合計は、前の行とその前の行(「前の2行目」)に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つのレコードの印刷を遅らせます。現在の2番目のフィールドが前のフィールドと同じかどうかに基づいて、印刷するレコード(現在または以前)を選択します。現在のレコードが印刷されてもprev更新されません。同じ 2 番目のフィールドを持つ連続レコードは事実上 1 段階上に移動し、最初のレコードは最後の位置に移動されます。最後に、残りのレコードを印刷します(入力に存在する場合)。

この「転送」を最大連続レコード数(スワップペアのみ)に制限するには、に変更するswap = $2 == p2だけですswap = $2 == p2 && f++ < 2

関連情報