2つのファイルがあります。
F1.txt
:
a:erwer|ee
b:eeeee|eee
c:ewrew|erwe
そしてF2.txt
:
a.T1
a.T2
b.T3
C.T7
c.T4
F2.txtでF1.txtをチェックして、キーワードのa
発生b
回数を確認する必要があります。c
F1.txtの予想出力:
a:erwer|ee:total:2
b:eeeee|eeet:total:1
c:ewrew|erwe:total:2
他のファイルのo / pを更新します。
a:2
b:1
c:2
答え1
ファイルが大きすぎない場合は、awkを使用できます。
awk '
BEGIN{FS=".";OFS=":"}
NR==FNR{a[tolower($0)]=0;next}
{
if(tolower($1) in a){
a[tolower($1)]++
}
}
END{
for(key in a){
print key, a[key]
}
}
' F1.txt F2.txt
大文字と小文字を区別するコンテンツが必要な場合は、そのtolower
機能を削除してください。
修正された質問に:
awk '
BEGIN{FS="[:.]";OFS=":"}
NR==FNR{l[tolower($1)]=$0;cpt[tolower($1)]=0;next}
{
if(tolower($1) in cpt){
cpt[tolower($1)]++
}
}
END{
for(key in cpt){
print l[key],"total",cpt[key]
}
}
' F1.txt F2.txt
答え2
まずF2.txt
、ファイルをフィールド区切り文字でドット付きのCSVファイルとして扱います。最初のフィールドを小文字に変換してから、最初のフィールドの固有値の数を計算できます。使用してミラー次の()で一意mlr
の最初のフィールド値とその数は、必要に応じて区切り文字として出力されます。:
$ mlr --csv --ifs . --ofs : -N put '$1 = tolower($1)' then count -g 1 F2.txt
a:2
b:1
c:2
この結果をフィルタリングしてコンテンツとマージするには、次のようにF1.txt
しますjoin
。
$ mlr --csv --ifs . --ofs : -N put '$1 = tolower($1)' then count -g 1 F2.txt | join -t : F1.txt -
a:erwer|ee:2
b:eeeee|eee:1
c:ewrew|erwe:2
total:
末尾の数字の前に文字列を挿入するには、文字列をsed 's/.*:/&total:/'
。
$ mlr --csv --ifs . --ofs : -N put '$1 = tolower($1)' then count -g 1 F2.txt | join -t : F1.txt - | sed 's/.*:/&total:/'
a:erwer|ee:total:2
b:eeeee|eee:total:1
c:ewrew|erwe:total:2
使用されたバリエーションawk
:
$ awk -F : 'FNR==NR { split($0,a,"."); count[tolower(a[1])]++; next } $1 in count { printf "%s:total:%s\n", $0, count[$1] }' F2.txt F1.txt
a:erwer|ee:total:2
b:eeeee|eee:total:1
c:ewrew|erwe:total:2
コードawk
:
BEGIN { FS = ":" }
FNR==NR {
split($0,a,".")
count[tolower(a[1])]++
next
}
$1 in count {
printf "%s:total:%s\n", $0, count[$1]
}
おもしろい:MillerのDockerイメージを使って上記の最初のコマンドを実行する方法を教えてください。
docker run --rm -i jauderho/miller:latest \
--csv --ifs . --ofs : -N \
put '$1 = tolower($1)' then count -g 1 <F2.txt
...でも...
alias mlr='docker run --rm -i jauderho/miller:latest'
mlr --csv --ifs . --ofs : -N put '$1 = tolower($1)' then count -g 1 <F2.txt
答え3
Perlはawkや@terdonなどのさまざまなツールパイプラインで実行できますが、これには適しています(しかし、その解決策の部分文字列に関する警告に注意してください)。
以下は、いくつかの入力例です。
ファイル1
a
b
c
d
ファイル2
a.T1
a.T2
b.T3
C.T7
c.T4
そして例を実行してみてください:
% perl count.pl file1 file2
a:2
b:1
c:2
d:0
これは比較的構造化されたPerlバージョンです。より明示的なファイル処理の代わりに、言語の「魔法のreadline」機能を使用します。エラー処理機能はありませんが、実行時に自動的に提供されます。 「slurpモード」を使用してキーファイルを読み取るため、サイズとメモリに関する警告はそのファイルには価値があるかもしれませんが、探しているキーファイルが非常に大きいかどうか疑わしいです。すべてをメモリにインポートせずに1行ずつ正確に繰り返すので、file2
任意に大きくすることができます。正規表現の一致を使用するので、最後の「。」上記の内容はすべて一致します。 'T'はの単一の数字と一致することができますfile1
。
#!perl
use warnings;
use strict;
sub get_keys {
local $/ = undef;
return { map { lc $_ => 0 } split /\n/, <> };
}
sub count {
my $m = shift;
while (<>) {
if ( m/(.*)\.T\d/ ) {
$m->{lc $1}++ if exists $m->{lc $1}
}
}
return $m;
}
sub output {
my $m = shift;
for my $k ( sort keys %$m ) {
printf "%s:%d\n", $k, $m->{$k};
}
return;
}
output count get_keys;
これは同じコアロジックを使用するPerlのより短く使い捨てバージョンです。
#!perl
use warnings;
use strict;
undef $/;
my %map = map { lc $_ => 0 } split /\n/, <>;
$/ = "\n";
while (<>) {
if ( m/(.*)\.T\d/ ) {
$map{lc $1}++ if exists $map{lc $1}
}
}
for my $k ( sort keys %map ) {
printf "%s:%d\n", $k, $map{$k};
}
同じ操作を実行しても読みにくくない短いコマンドラインバージョンが必要な場合は、これに適した2番目のバージョンがあります。
% perl -e '
undef $/;
%m = map { lc $_ => 0 } split /\n/, <>;
$/ = "\n";
while (<>) { if ( /(.*)\.T\d/ ) { $m{lc $1}++ } }
for $k ( sort keys %m ) { printf "%s:%d\n", $k, $m{$k}; }
' file1 file2
a:2
b:1
c:2
d:0
または、BEGIN ブロックと END ブロックで awk などの入力自動ループを使用します。
% perl -ne '
BEGIN {
undef $/;
%m = map { lc $_ => 0 } split /\n/, <>;
$/ = "\n";
}
if ( /(.*)\.T\d/ ) { $m{lc $1}++ }
END {
for $k ( sort keys %m ) { printf "%s:%d\n", $k, $m{$k}; }
}
' file1 file2
a:2
b:1
c:2
d:0
この回答のすべてのコード例は、同じプログラム用です。どちらも同じ入力から同じ出力を生成します。それらは実質的に書き直されなかった。これはわずかな調整だけを除いて、同じ言語を使用するのと同じ論理です。
ソリューションを評価する際に従う必要のある軸は、後でメンテナンスのためにソリューションに戻るのがどれほど簡単かです。これがこの1つの操作だけを実行する簡単なツールであり、それを繰り返し実行する必要がありますが、コードを別のものに拡張する必要があるかどうか疑問がある場合は、おそらく2番目のバージョンまたはそれに近いバージョンを使用します。これがより大きなプログラムの一部である場合は、最初のプログラムから始めて、より明示的なファイル処理(例:)open
とclose
エラー処理を追加します。