次の形式の1,200万行を含むCSVファイルがあります。
mcu_i,INIT,200,iFlash, 11593925, 88347,,0x00092684,r,0x4606b570, ok,, 32,single,op-c,0,, 0, 0, 0,
mcu_i,INIT,200,iFlash, 11593931, 88348,,0x00092678,r,0x28003801, ok,, 32,single,op-c,0,, 0, 0, 0,
次のロジックを使用して、6番目の列の値に基づいて行を削除したいと思います。 if (value >= X AND value <= Y ) => 行の削除
gawkを使って解決策を見つけました。
gawk -i inplace -F ',' -v s="$start_marker" -v e="$end_marker" '!($6 <= e && $6 >= s)' myfile.csv
しかし、時間がかかりすぎるので、より良いパフォーマンスを持つ他のソリューションが欲しいです。
ありがとう
答え1
長い話を短く
標準出力をgawk
リダイレクト/dev/null
またはパイピングすると、速度がcat
大幅に向上し、実行時間が大幅に短縮されます。
gawk -i inplace [...] myfile.csv >/dev/null
または:
gawk -i inplace [...] myfile.csv | cat
水に飛び込む
@RomeoNinovの場合でも回答元のコマンドよりも実際に速く実行されます。説明したいです。なぜを使用しても高速です-i inplace
。
見たら対話型および非対話型バッファリング部分的にgawk
情報ページ、次の内容が表示されます。
インタラクティブプログラムは平均レベルです。ラインバッファ出力します(つまり、各行を作成します)。非対話型プログラムは、バッファがいっぱいになるまで待ちます。これは複数行の出力にすることができます。
gawk
結果が一部の「in-place」で印刷されても標準出力として印刷されない場合も同様です。
はい
10行のファイルがあります。
$ cat somefile
1
2
3
4
5
6
7
8
9
10
デフォルトでは(ファイルを変更せずにすべての行をそのまま印刷します)、10回のstrace
システムコールが実行されます(ソースファイルの各行に1つずつ)。gawk
write
$ strace -e trace=write -c gawk -i inplace 1 somefile
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00 0.000098 9 10 write
------ ----------- ----------- --------- --------- ----------------
100.00 0.000098 9 10 total
これは対話的に実行され、結果が次のようになるためです。ラインバッファ(gawk
結果が標準出力ではなくファイルに書き込まれても、各行は完了するとすぐに印刷されます。)
stdoutを非対話型にするためにコマンドにリダイレクト/dev/null
(またはパイプで接続)すると、単一のシステム呼び出しのみが呼び出されるように見えます。これは、すべての行をすぐに印刷せず、バッファがいっぱいになった場合にのみ結果をフラッシュするためです。cat
strace
gawk
write
$ strace -e trace=write -c gawk -i inplace 1 somefile > /dev/null
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00 0.000020 20 1 write
------ ----------- ----------- --------- --------- ----------------
100.00 0.000020 20 1 total
もちろん、これは累積的であり、入力ファイルが大きいほど、対話型実行と非対話型実行との差が大きくなります。
一般化する
gawk
対話モードでは、処理が完了すると各行がファイルに書き込まれるため、コマンドは遅くなります。これは、ファイルに何百万回もの書き込みを行うことを意味します。
@RomeoNinovのソリューションは使用しないため、元のコマンドよりも高速ですが、出力を一時ファイルにリダイレクトするため、inplace
非対話型モードで実行され、バッファフラッシュを最適化し、ファイルgawk
操作の書き込み操作を少なくします。
しかし、質問に提供されたコマンドは引き続き使用できますが、stdoutを/dev/null
(とにかく空にするため)リダイレクトまたはパイプするとcat
すぐに実行されます。
gawk
次のように使用する場合のセキュリティリスクinplace
私は自分の仕事でうまくいくと予測不可能な結果をもたらす可能性があるという@RomeoNinovのコメントに全く同意しませんが、@OlivierDulacのコメント-i inplace
これは、一般的な使用がセキュリティの脆弱性と見なされる理由を説明する便利な答えを提供します。この問題を解決する方法安全な方法で実行してください。
答え2
1つの可能な方法は(コマンドを書き換えることによって)次のようになります。
gawk -F, -v s="$start_marker" -v e="$end_marker" '$6 > e || $6 < s' myfile.csv >/tmp/newfile
現場でawk
の操作は推奨されず、安全上の危険があります。また、スクリプトが正しいと100%確信する前にソースファイルを台無しにすることもできます。
答え3
そうでない場合は、awk
Perlを試してみてください。
#!/usr/bin/perl
use 5.18.2;
use warnings;
use strict;
my ($X, $Y) = (88347, 88347);
while (<>) {
next
if (/(?:^[^,]*,){5}\s*([^,]+)/ && $1 >= $X && $1 <= $Y);
print;
}
正規表現は、1行から最初の5つのカンマ区切りフィールドをスキップし、スペースを無視して6番目のフィールドの残りの部分をキャプチャします$1
。条件が一致すると、その行は無視されます。それ以外の場合、これは出力です。
たとえば、値が88348の行を出力します。
perl your_script input_file(s) > output_file
.
答え4
使用幸せ(以前のPerl_6)
~$ raku -MText::CSV -e 'my @rows; my $csv = Text::CSV.new( sep => ","); \
while ($csv.getline($*IN)) -> $row { @rows.push: $row.map(*.trim) if 88000 < $row.[5] < 98000; }; \
.join(",").put for @rows;' < ~/raphui_771255.csv
RakuはPerlファミリーのプログラミング言語です。 Unicodeと強力な正規表現エンジンの高度なサポートを提供します。
上記の答えはRakuのText::CSV
モジュールを使用しています。 Perl(5)モジュールはText::CSV_XS
良い反応を得ており、長い間モジュールの作成者/メンテナンス者がText::CSV
Raku(H. Merijn Brand、パーソナルコミュニケーション)用のモジュールを開発してきました。
入力例(@aborrusoに感謝!):
mcu_i,INIT,200,iFlash, 11593925, 88347,,0x00092684,r,0x4606b570, ok,, 32,single,op-c,0,, 0, 0, 0,
mcu_i,INIT,200,iFlash, 11593931, 88348,,0x00092678,r,0x28003801, ok,, 32,single,op-c,0,, 0, 0, 0,
mcu_i,INIT,200,iFlash, 10593931, 88348,,0x00092678,r,0x28003801, ok,, 32,single,op-c,0,, 0, 0, 0,
mcu_i,INIT,200,iFlash, 21593931, 98348,,0x00092678,r,0x28003801, ok,, 32,single,op-c,0,, 0, 0, 0,
mcu_i,INIT,200,iFlash, 31593931, 108348,,0x00092678,r,0x28003801, ok,, 32,single,op-c,0,, 0, 0, 0,
出力例:
mcu_i,INIT,200,iFlash,11593925,88347,,0x00092684,r,0x4606b570,ok,,32,single,op-c,0,,0,0,0,
mcu_i,INIT,200,iFlash,11593931,88348,,0x00092678,r,0x28003801,ok,,32,single,op-c,0,,0,0,0,
mcu_i,INIT,200,iFlash,10593931,88348,,0x00092678,r,0x28003801,ok,,32,single,op-c,0,,0,0,0,
注:Rakuは「接続された」不平等を許可します。 Rakuには、ハードコーディングされた値の代わりに%*ENV
シェル変数にアクセスするために使用できる特別な関連配列もあります。したがって、次は環境(つまりシェル)からシェルstartMarker
変数を取得しますstopMarker
。出力に高度なcsv( …, out => $*OUT)
機能を使用すると、スペースを含む文字列が自動的に引用されます(あなたの場合は.trim
呼び出しも削除してください)。
~$ env startMarker="88000" stopMarker="89000" \
raku -MText::CSV -e 'my $start = %*ENV<startMarker>; my $stop = %*ENV<stopMarker>; \
my @rows; my $csv = Text::CSV.new( sep => ","); \
while ($csv.getline($*IN)) -> $row { @rows.push: $row if $start < $row.[5] < $stop; }; \
csv(in => @rows, out => $*OUT);' < ~/raphui_771255.csv
mcu_i,INIT,200,iFlash," 11593925"," 88347",,0x00092684,r,0x4606b570," ok",," 32",single,op-c,0,," 0"," 0"," 0",
mcu_i,INIT,200,iFlash," 11593931"," 88348",,0x00092678,r,0x28003801," ok",," 32",single,op-c,0,," 0"," 0"," 0",
mcu_i,INIT,200,iFlash," 10593931"," 88348",,0x00092678,r,0x28003801," ok",," 32",single,op-c,0,," 0"," 0"," 0",
https://raku.land/zef:Tux/Text::CSV
https://github.com/Tux/CSV/blob/master/doc/Text-CSV.md
https://docs.raku.org
https://raku.org