web-dev-qa-db-ja.com

ファイルの3行目が非常に多数の行を含んでいる後に2行ごとに削除する方法は?


お気に入り
私が持っている場合:

1st line (keep)  
2nd line (keep)  
3rd line (keep)  
4rth lines (delete)  
5th (del)  
6th (keep)  
7nth (keep)  
8th lines  (keep)  
9th (del)  
10th (del)  
11th (keep)  
12th (keep)  
13th (keep)  
14th (del)  
15th (del)  

等....

5
Jaguar Jom

試してください:

awk '(NR-1)%5<3' file

例えば:

$ awk '(NR-1)%5<3' file
1st line (keep)
2nd line (keep)
3rd line (keep)
6th (keep)
7nth (keep)
8th lines (keep)
11th (keep)
12th (keep)
13th (keep)

使い方

コマンド(NR-1)%5<3は、awkに、(NR-1)%5<3がtrueである行を出力するように指示します。 awkでは、NRは行番号で、最初の行は1としてカウントされます。ファイルの5行ごとに、そのステートメントは最初の3行に当てはまります。

13
John1024

簡単なコマンドは次のとおりです。

awk '{if((NR-1) % 5<=2){print $0}}' file

最初の3行だけを5行ずつ印刷します。 (NR-1)%50 1 2 3 4のような出力を提供し、最初の3行は2以下であるため、出力するだけです。

内容のファイルがあります:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

出力は次のとおりです。

1
2
3
6
7
8
11
12
13

または、コメントで提案されているように、使用できます:

awk '(NR - 1) % 5 <= 2' file
6
Prvt_Yadav

基本的に、あなたはawkで 'Fizz-Buzz'のようなものを望みます...

awk '{ if (i++%5 < 3) print $0;}'

この作品を表示するには...

for x in 1 2 3 4 5 6 7 8 9 10 ; do echo $x; done |
awk '{ if (i++%5 < 3) print $0;}'

ファイルの名前が 'mybigfile.csv'の場合、

awk '{ if (i++%5 < 3) print $0;}' < mybigfile.csv > mybigfile-123.csv
5
ChuckCottrill

これは、GNU sedを使用して解決できます。

_sed '4~5,5~5d' file
_

これは、sed標準にGNU固有の拡張機能を使用しているため、たとえば、 BSDはmacOS上で動作します。ただし、GNU sedはbrewを使用してmacOSにインストールでき、その後gsedとして使用できます。LinuxではGNUデフォルトはsedです。

これにより、5行ごとに4行目から5行目までに収まらないすべての行が印刷されます。より明確な例:_sed '3~10,6~10d'_ 3行目から6行目までを削除して、10行の各グループの選択行1、2、7、8、9、10を塗りつぶします。

トップ投票の回答は、awk '(NR-1)%5<3'の使用を提案しています。私のマシンでは、1から200万までの数字を含むファイルで、これは約0.6秒かかりますが、この回答のsedソリューションは約0.35秒かかります。 sedは一般に単純なツールであるため、これは合理的であり、したがって、より複雑であるがフル機能を備えたawkよりも高速に動作できます。

5
tomsmeding

ファイルから特定の行のパターンをマスクするための一般的なソリューション:

_#!/bin/sh

# The pattern is given on the command line.
pattern=$1

# The period is simply the length of the pattern.
period=${#pattern}

# Use bc to convert the binary pattern to an integer.
mask=$( printf 'ibase=2; %s\n' "$pattern" | bc )

awk -v mask="$mask" -v period="$period" '
    BEGIN { p = lshift(1, period-1) }
    and(rshift(p, (FNR-1) % period), mask)'
_

これは、非標準関数and()(ビットごとのAND)、rshift()およびlshift()(ビットごとの右および左シフト)を実装するawkに依存しています。どちらもGNU awkawkの一部のBSD実装はありますが、mawkではありません。

これは、周期的な周期と各周期内のどの行を保持またはマスクするかを表す2進数であるパターンを取ります。 _1_は「保持」を意味し、_0_は「削除」を意味します。

例:質問に適用する必要がある行のパターンは_11100_です。これは、「5行のセットごとに、最初の3行を保持し、他の行を削​​除する」ことを意味します。

_01001000_を使用すると、8行ごとに2行目と5行目以外がすべて削除されます。

awkプログラムは、次のようにBEGINブロックなしで書くこともできます。

_and(lshift(1, (period-1) - (FNR-1) % period), mask)
_

_(period-1) - (FNR-1) % period_の位置による1の左シフトは、2の累乗の計算と同じですが、awkは正確な整数演算ではなく浮動小数点演算を使用して演算を行うため、lshift()を使用しています。

コードはパターンのバイナリ表現に依存しているため、非常に長いパターンはうまく機能しない可能性があります。

テスト:

削除したい行を削除する:

_$ sh script.sh 11100 <file
1st line (keep)
2nd line (keep)
3rd line (keep)
6th (keep)
7nth (keep)
8th lines  (keep)
11th (keep)
12th (keep)
13th (keep)
_

パターンを反転させる:

_$ sh script.sh 00011 <file
4rth lines (delete)
5th (del)
9th (del)
10th (del)
14th (del)
15th (del)
_
4
Kusalananda

以下のコマンドで試してみましたが、うまくいきました

for((i=1;i<=20;i++)); do  j=$(($i+2)); sed -n ''$i','$j'p' filename;i=$(($j+2)); done

出力

1st line (keep)
2nd line (keep)
3rd line (keep)
6th (keep)
7nth (keep)
8th lines (keep)
11th (keep)
12th (keep)
13th (keep)
1