-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkeycommon
114 lines (94 loc) · 6.11 KB
/
keycommon
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#!/usr/bin/perl
use 5.001 ; use strict ; use warnings ;
use Getopt::Std ; getopts '0:12:cf:nr_~/:' , \my%o ;
use Scalar::Util qw/looks_like_number/ ;
use Term::ANSIColor qw/:constants/ ; $Term::ANSIColor::AUTORESET = 1 ;
my $sep = $o{'/'} // "\t" ; # 入出力の区切り文字
my $empty = $o{0} // 'undef' ; # 対応する値が無い場合の代替の値
my $cutpos = $o{f} // 1 ; # 各行を左から何番目の列で切るか
my %val ; # $val{ キーの値 } [ ファイル番号 ] = バリューの値
my $pole = 0 ;
my $Flst = (defined $o{2} && $o{2} =~ m/\./ ) ; # 各ファイルで同じキーが出現した場合に最後のバリューを採用するかどうかのフラグ
my $Fcon = (defined $o{2} && $o{2} =~ m/:/ ) ; # 各ファイルで同じキーが出現した場合に最後のバリューを採用するかどうかのフラグ
my (@keg1,%keg1) ; # -1 が指定された場合、キーが最初に読まれた順に、出力の順序を一致させる、ために使う変数
&reading ;
&outputting ;
exit 0 ;
sub reading {
while ( <> ) {
chomp ;
do { ++ $pole ; next } if eof || m/^$/ ; # 複数のデータセットに対する処理
my @F = split /$sep/ , $_ , $cutpos + 1 ;
my $key = join $sep , splice @F , 0 , $cutpos ;# 先頭のみ束ねる。
my $value = $F[0] // '' ; # 残ったものを束ねる。
( $key , $value ) = ( $value , $key ) if $o{'~'} ;
do { push @keg1 , $key unless $keg1 { $key } ++ } if $o{1} ;
if ( $Flst ) { $val{ $key } [ $pole ] = $value } # キー(1列目)ごとに ファイル番号を表す $pole ごとに値(2列目)を格納。
elsif ($Fcon) { grep { if ( defined $_ ) { $_ .= ":$value" } else { $_ = $value } } $val{ $key } [ $pole ] }
else { $val{ $key } [ $pole ] //= $value } ; # キー(1列目)ごとに ファイル番号を表す $pole ごとに値(2列目)を格納。
}
}
sub outputting {
my @keg = keys %val unless $o{1} ;
@keg =
$o{1} ? @keg1 :
$o{n} ?
(@{[sort {$a <=> $b} grep { looks_like_number($_) } @keg ]} ,
sort {$a cmp $b} grep { ! looks_like_number($_) } @keg ) :
sort @keg ;
@keg = reverse @keg if $o{r} ;
*UNDERLINE = sub {@_} unless $o{'_'} ;
for my $k ( @keg ) {
print scalar @{[grep {defined $_} @{$val{$k}} ]}, "\t" if $o{c} ;
print UNDERLINE $k ;
print join $sep , '' , map { $val{$k}[$_] // $empty } 0 .. $pole -1 ;
print "\n" ;
}
}
sub VERSION_MESSAGE {}
sub HELP_MESSAGE{
$0=~s|.*/|| ; $ARGV[1] //= '' ;
while(<DATA>){
s/\$0/$0/g ;
print $_ if $ARGV[1] =~ /opt/ ? m/^\ +\-/ : s/^=head1// .. s/^=cut// ;
}
exit 0 ;
}
__END__
=encoding utf8
=head1
$0
入力:
1列目はキーで2列目は参照値を持つ、空行区切りまたは複数のファイルによる
複数のデータセット。
ただし、ひとつひとつのデータセットにおいて、キーは全て異なるとしている。
出力:
1列目はキーの合併集合。i行目1列目はキーの値 k[i] 。
i行 j+1列目の値は、j番目のデータセットにおける
キー k[i] に対する参照値となる。
オプション:
-/ str : 区切り文字をタブから指定した変更する。
-~ : キーとバリューを反転する。(左側をバリュー、右側をキーとみなす。)
-_ : キーに下線を引く。ANSIカラーエスケープコードを使用している。
-c : 各キーに該当するファイルの個数を行の先頭に表示する。(このことで共通集合を検出するトリッキーなこともできる。)
-f num : キーとバリューを分離する位置を指定する。未指定なら1であり、その場合、最左列とそれ以外に分ける。
-n : キーの出力順序に関して、最初に数値については数の順序でソートし、その次に数値以外を文字列で整列する。
-r : キーの出力順序を逆にする。
-0 str : 空欄に埋める文字列。たとえば0を指定する。未指定ならundef。
-1 : 出力する順序に関して、キーの順番は、入力で読み取るときに最初に読みとったキーの順に一致させる。(-r指定があれば逆になる。)
-2 str : 1つのデータセット(通常はファイル)に、同じキーが2回以上現れた場合の挙動を str で指定。
-2 1 : パラメータ文字列に 1の文字が含まれていたら、同じファイルで同じキーに2番目以降に出現した値は初めの値を採用する。
-2 . : パラメータ文字列に ピリオド(.) が含まれていたら、同じファイルに同じキーに2番目以降に出現した値は最後の値を採用する。
-2 : : パラメータ文字列に コロン(:) が含まれていたら、同じファイルに同じキーに現れる全ての値が : で連結される。(<-- あまりきれいでないかも)
--help : このヘルプを表示する。
その他:
* 出力は クロス表作成によく似ているが、クロス表の場合は、2列のデータを同じペア毎に集計している。
$0 は、2列のデータをデータセットの番号と、キーのペアで集計している。
* トリッキーな使い方であるが、-f 100 など十分大きな数を設定することで、各ファイルの行についての合併集合を出力できる。
開発上のメモ:
* このプログラムは 2016年3月15日から4月6日にかけて集中的に作成。作者は下野寿之。有用なコマンドと考えていたが、
コマンド名をなかなか決めることが出来なかったので公開を2018年7月23日までしてこなかった。他のコマンドとの
オプションの整合性を考え、いくつか修正し、さらにいくつか新たなオプションを追加した。
GPL3のライセンスを指定している。
* 他に追加する機能が見つからなければ、このマニュアルを英語化する。
=cut