この形式のデータファイルがあります
1 4
2 0
2 3
3 5
5 3
8 12
2 3
3 5
5 3
0 -1
2 4
33 3
ファイルには12行があり、それぞれ3行の4つの連続ブロックとして解釈する必要があります。たとえば、3番目のブロックは
2 3
3 5
5 3
mからnまでの各ブロックからiからjまでの行を抽出してLinuxでファイルに出力する方法は?
たとえば、、、、の場合、i=2
目的のj=3
結果m=1
はn=3
次のようになります。
2 0
2 3
5 3
8 12
3 5
5 3
ありがとうございます。
答え1
次のawk
プログラムは、次のことを行う必要があります。
awk -v bs=3 -v i=2 -v j=3 -v m=1 -v n=3 '(FNR/bs>m-1) && (FNR/bs<=n) && ((FNR-1)%bs>=i-1) && ((FNR-1)%bs<j)' input.txt
これにより、キーデータがawk
変数としてプログラムにインポートされます。
- 変数としてのブロックサイズ
bs
- 開始および終了ブロック番号を変数として使用
m
します。n
- 開始行と終了行番号を変数として使用
i
します。j
これは、非ゼロと評価された「ルール」ブロックの外側のすべての条件が現在の行を印刷するように指示するawk
ロジックを使用します。true
awk
FNR
印刷は、各ファイルの行カウンタを表す自動変数に基づいています。あなたの要件は、基本的に数字をブロックサイズで割ってブロック番号を識別し、FNR
計算モジュラスでブロック内の行を識別しFNR
(便宜上、ゼロから始めて使用する)印刷したい行にのみ適用されます。FNR-1
true
修正する
時間のかかる作業をできるだけ避けることで、プログラムのスピードを上げることができます。これを行うには、次のようにプログラムを変更します。
awk ... 'BEGIN{first=bs*(m-1)+1; last=bs*n}
FNR<first{next}
FNR>last{exit}
((FNR-1)%bs>=i-1) && ((FNR-1)%bs<j)' input.txt
これにより、最初に考慮すべき最初の行と最後の行が決定されます。
- 現在の行番号が最初のブロックの先頭より前の場合は、すぐに次の行にジャンプして印刷するかどうかを「きめ細かく」確認するための計算と比較は行われません。
- 同様に、現在の行番号が考慮すべき最後のブロックを超えると、プログラムは直ちに終了します。
- 「関心領域」内にある場合にのみ、印刷する行を確認するために算術演算が実行されます。
これにより、計算作業を最小限に抑えることができます。
GNUバリアントを使用awk
して複数の入力ファイルを引数として指定した場合は、プログラムを終了するのではなく、次のファイルに移動するために代わりnextfile
に使用してください。exit
答え2
GNU sedとawkを使用した代替ソリューション:
# Split data into data-blocks
<infile sed '3~3G' |
# Only pass blocks m through n onwards
awk 'NR >= m && NR <= n' RS= ORS='\n\n' m=1 n=3 |
# Only print lines i through j within each block
awk '{ for (x=i ; x<=j; x++) print $x }' RS= FS='\n' i=2 j=3
答え3
別のストリームオプション(-s)でGNU sedを使用すると、コマンドラインで複数のファイルを別々のファイルとして扱うことができます。
i=2 j=3 m=1 n=3 G=3
split -l "$G" file
printf '%s\n' x?* |
sed -e "$m,$n!d;${n}q" |
xargs sed -s "$i,$j!d"
答え4
Raku(以前のPerl_6)の使用
raku -e '.join("\n").put for lines.rotor(3)[0..2].map(*.[1..2]);'
入力例:
1 4
2 0
2 3
3 5
5 3
8 12
2 3
3 5
5 3
0 -1
2 4
33 3
出力例:
2 0
2 3
5 3
8 12
3 5
5 3
上記は、Perlプログラミング言語ファミリーであるRakuで書かれたソリューションです。つまり、一緒にlines
読み込み(ゆっくり)rotor
-ed(つまり、グループ化)3
すると、各行が正しくグループ化されます(部分グループを返したい場合は、末尾の不完全なグループは上記のコードで削除されます。正しい「副詞」オプションを追加してください)としてrotor(3, :partial)
:)。
3行目ごとに最初の3つのグループが保持され([0..2]
インデックス構成を使用)、3つのmap(*.[1..2])
グループ内でマッピングが実行され、2番目と3番目の要素のみが返されます(RakuインデックスなどのPerl系言語は0から)。
上記のコードは目的の結果を返しますが、プログラマが望む場合は、次のように行/参照で結果を返すことができます。
raku -e '.raku.put for lines.rotor(3)[0..2].map(*.[1..2]);' file
("2 0", "2 3")
("5 3", "8 12")
("3 5", "5 3")