2007-11-30
■[Netnews]マッチした特定の場所を抜き出す
以下のような場合にマッチした数値だけを抜き出したいと言うものです。
$echo "test4325363test" | awk "/(.*)([0-9]+)(.*)/ {print NUMBER HERE!}"
代表的なものに match() を使うのと gensub() を使うものがあります。
以下は match() を使ったものです。
echo "test4325363test" | awk '
match($0,/[0-9]+/) {
print substr($0,RSTART,RLENGTH)
}
'
そして gensub() を使ったものです。
gawk '{print gensub(/(.*)([0-9]+)(.*)/,"\\2","")}'
■[Netnews]範囲内の使っていない数字を求める
MIN_VALUE=800 MAX_VALUE=810
という数字の範囲で、以下のファイルから使っていない数字を求めるというものです。
#> cat all_values 100 200 800 801 803 900
以下のようなコードを書いたので、最適化してくれまいかということです。
#> nawk -v MIN_VALUE=$MIN_VALUE -v MAX_VALUE=$MAX_VALUE '{
if(($1 >= MIN_VALUE) && ($1 <= MAX_VALUE)) {
VAL[$1] = $1 ;
}
} END {
for (i=MIN_VALUE ; i<=MAX_VALUE ; i++) {
if (VAL[i] == "") {
print "result: " i ;
exit ;
}
}
}' all_values
result: 802
つまり変数の使用を確認するということに落とし込めば以下のような awk の使い方にできます。
if (VAL[i] == "") {
とか
if (i in VAL) {
といったものです。
あと範囲内の計算をするライブラリとして以下のようなものもあるそうです。awk でも最大級のスクリプトかも?
- 範囲内の計算
- 上記を使った例
全体をまとめたものが以下のものです。
$ awk -v MIN_VALUE=$MIN_VALUE -v MAX_VALUE=$MAX_VALUE '
BEGIN{
for(i=MIN_VALUE;i<MAX_VALUE;i++){a[i]}
}
MIN_VALUE>$1{next}
MAX_VALUE<$1{next}
{delete a[$1]}
END{
for(f in a){
print f;
exit}
}
' all_values
802
nawk -v MIN_VALUE=$MIN_VALUE -v MAX_VALUE=$MAX_VALUE '
$1 >= MIN_VALUE && $1 <= MAX_VALUE { VAL[$1] }
END {
for (i = MIN_VALUE; i in VAL; i++)
; # nothing to do here, just iterate
if (i <= MAX_VALUE) # or simply print i without if(),
print i # depending on requirements if
# all positions already occupied
}
'
■[Netnews]テキストにある文字列ブロックを挿入する
1 test1 1 test2 1 test3 2 test1 2 test2 2 test3 2 test4 3 test1 3 test2 3 test3
というファイルを以下のようにしたいというものです。
1 beg
1 test1
1 test2
1 test3
1 end
2 beg
2 test1
2 test2
2 test3
2 test4
2 end
3 beg
3 test1
3 test2
3 test3
3 end
||<<
そこで無理して作ったのか以下のようなもので、これを直して欲しいというものです。
>|awk|
awk '{
old=new
new=$1
}
$1 != old {
if (oline) print oline
newl=$0
$2="beg"
print $0
print newl
old=$1
next
}
$1 == old { print; $2="end"; oline=$0 }
END {print oline}
'
回答は以下のようなものです。
$1 != prev { print (prev ? prev " end" ORS : "") $1 " beg"; prev=$1 } 1 END { print prev " end" }
$1 != prev { pre() } $1 != prev { print $1, "beg"; prev = $1 } 1 END { pre() } function pre() { if (prev) print prev, "end" }
$1 != prev { pre(); print $1, "beg"; prev = $1 } 1
one!=(now=$1){print (NR==1?"":"end"ORS)"beg"ORS $0;one=now;next} 1 END{print "end"}
■[Netnews]awk できちんとマッチしない
awk '/^----/,/^____/{next}{print}'
というものが正常に動作していないというものです。具体的には、以下のようなファイルを使っています。
From - Sun Sep 18 12:55:25 2005 (...) Some text I want to keep Some text I want to keep ------------------------------------------------------- SF.Net email is sponsored by: Tame your development..... ________________________________________ this text should stay email@address https://address/should/stay From - Sun Sep 18 12:58:18 2005 (...) 2 Some text I want to keep 2 2 Some text I want to keep 2 A cool diagram which needs to stay:
回答は以下のようなものです。
$ awk '/^-+$/,/^_+$/{next}{print}'
$ awk '/^-+$/,/^_*$/{next}{print}'
{a[NR]=$0}
/^-+$/{from=NR}
/^_+$/{to=NR}
END{ if(from < to) {
for(i=1;i<from;i++) print a[i]
for(i=to+1;i<NR;i++) print a[i]
}
}
awk 1 RS="-+\n[^-]**_+\n" filename
awk 1 RS="-+[^-]*_+" filename
しかし、awker という種族は Golf が好きですね。
■[Netnews]tac の代用
最後から先頭へ向かって処理したいので、tac の代用はないかということですが、sed で以下のようなものを使っているそうです。
sed -e '1!G;h;$!d'
以下の Juergen Kahrs の回答はどうかと思いますけど・・・。
awk '{print NR, $0}' data_file.txt | sort -n -r | awk '{$1=""; print $0}'
ついでに Ed Morton も・・・。
awk '{print NR, $0}' data_file.txt | sort -n -r | awk 'sub(/[[:digit:]]+ /,"")'
awk の中でクローズするなら以下のように書きますが、tac の代用は Perl が良いかと思います。
gawk '{a[NR]=$0}END{for(i=NR;i>0;i--){print a[i]}}' infile
■[Netnews]変数のセットに awk を用いる
set myvariable = `awk '{print $1}' cool`
としても動作しないのは何故かというものですが・・・結果が読めないぞ・・・shell が bash 系というオチでいいのかな?
myvariable=`awk '{print $1}' cool`
で動作したようですが、質問者が「空気読めよ。バカ」(意訳。オリジナルは "The rest of you are a bunch of narrow minded suckers.") といったのを機に盛り上がっていたのか・・・。
でもって、Ed が「その空気の読めないバカが藻前のために悩んでやってんじゃないか」(意訳。オリジナルは "You got exactly what us "narrow minded suckers" were trying to warn you about - something that seems to work but is in fact a poor "solution" to your actual problem") と微妙な大人の意見をしているのに噴いた。
■[Netnews]csv のヘッダーをどう扱うか
awk に csv のフィールドの位置ではなく、ヘッダーで一致させることができるかというものです。
awk '( $4 ~ /ASPAC/) {do something;}' < input_file
ではなく
awk '( region_name ~ /ASPAC/) {do something;}' < input_file
のようなものです。
回答は以下のようなもんが上がっていますが、csv という部分が最後以外欠落してないかい?
awk 'NR==1 {for (i=1;i<=NF;i++) f[$i]=i; next} $f["region_name"] ~ /ASPAC/) {do something}' input_file
awk 'NR==1 for (i=1;i<=NF;i++) { if ($i == "region_name") region_name = i else if ($i == "some_other_field") some_other_field = i else if .... next } }
BEGIN { FS=","} NR==1 { for(f=1;f<=NF;f++) m[$f]=f; next } $m["region_name"] ~ /ASPAC/
■[Netnews]フォーマットの問題
2007-10 14,807 1,604 29,600 2007-09 15,173 521 35,853 2007-08 12,799 1,236 516 2007-07 5,780 416 37,135
というファイルがあり、以下のように出力したいそうです。
2007-10 14,807 1,604 29,600 2007-09 15,173 521 35,853 2007-08 12,799 1,236 516 2007-07 5,780 416 37,135
そのためにやったことが以下のものです。
cat file| awk '{printf "%-11s%-7s%-9s%s\n",$1,$2,$3,$4}'
回答は以下のとおり。
$ awk '{printf "%7s%9s%9s%9s\n",$1,$2,$3,$4}' file
2007-10 14,807 1,604 29,600
2007-09 15,173 521 35,853
2007-08 12,799 1,236 516
2007-07 5,780 416 37,135
これを UUOC だと Ed は言っていますが、個人的には UUOC は悪くないと教えられてきたので、先頭に cat があっても悪いとあまり思っていなかったりします。先頭の cat は不要ですが、処理のオーバーヘッドが MS-DOS よりも少なく済む、つまりパイプを使うことは Unix の醍醐味のひとつだと師匠の一人に教わった記憶があります。既に 12 年前ですが・・・。
■[Netnews]数学問題
計算結果が awk だけ異なると言うものです。
$ echo "111111111 * 111111111" | bc -l
12345678987654321
$ perl -le 'print 111111111 * 111111111'
12345678987654321
$ python -c "print 111111111 * 111111111"
12345678987654321
(bash/zsh - same results)
$ echo $((111111111 * 111111111))
12345678987654321
$ awk 'BEGIN {printf "%.0f", '"111111111 * 111111111"'}'
12345678987654320
これは awk が倍精度浮動小数点で扱うことが原因です。
いろいろ試した結果が載っていて興味深いです。
on cygwin on Windows XP:
$ gawk 'BEGIN {print (111111111*111111111); exit}'
12345678987654320
on Solaris:
$ /usr/bin/awk 'BEGIN {print (111111111*111111111); exit}'
12345678987654320
$ nawk 'BEGIN {print (111111111*111111111); exit}'
12345678987654320
$ /usr/xpg4/bin/awk 'BEGIN {print (111111111*111111111); exit}'
12345678987654321
$ gawk 'BEGIN {print (111111111*111111111); exit}'
1.23457e+16
$ gawk 'BEGIN {printf "%.0f\n", (111111111*111111111); exit}'
12345678987654320
■[Netnews]ファイルのパース
10 abc 20 abcc 30 abcd 40 xycz 42 wxy 5 abc 20 abcd 30 abcc
"abc" - 10 "abcc" - 20 "abcd" - 30 ...
次のステップで
"abc" 10 - 5 = "abc" 5 "abcc" 20 - 30 = "abcc" -10
のようにしたいそうです。
回答は以下のとおりです。
awk '{a[$2]=(a[$2]=="" ? $1 : a[$2] - $1)} END{for (i in a) print i,a[i] }
{ if (a[$2]=="") {
a[$2] = $1
}
else {
a[$2] = a[$2] - $1
print $2, a[$2]
a[$2]=""}
}
{ if (a[$2]=="") {
a[$2] = $1
}
else {
a[$2] = a[$2] - $1
print $2, a[$2]
a[$2]=""}
}
END {
for (i in a) {
if(a[i]!=""){ print i,a[i] }
}
}
$2 in a { print $2, a[$2] - $1; delete a[$2]; next } { a[$2] = $1 } END { for (i in a) print i, a[i] }
■[Netnews]並べ替え
***record*** TAG1 A TAG2 B irrelevant------------------ TAG2 C TAG3 D TAG4 E # ***record*** TAG1 f TAG2 g TAG2 h irrelevant------------------ TAG3 i TAG4 j #
というファイルがあり、これを以下のようにしたいというものです。
A TAB B C TAB D TAB E f TAB g h TAB i TAB j
回答は以下のようなものが上がっています。
/^\*\*\*record/ { tags="" } /^#/ { print tags } /^irrelevant/ { next } { sep = (t!=$1 ? (tags=="" ? "" : "\t") : " ") } { tags = tags sep $2 ; t = $1}
■なんという無駄の無駄遣い
当然、違うところに萌えているわけですが、Linux のみなさん、jfbterm + vlc 256 color くらいで誰か作ってくれないかなぁ。
といいつつ、ちょっと作ってみたい。Excel に負けるわけにはいかない。(w
■中の人という立場って難しい
- Yokohama Linux Users Group Site - Welcome to Yokohama Linux Users Groupへようこそ-YLUG-メインコンテンツ(PukiWiki)
「第82回カーネル読書会」で話が出るかもしれない アレは私も設計を担当していた (微妙ではあるが、嘘ではない) ので、参加したい気もあるが、どうしようかな。中の人の立ち位置が難しい。
アレとか伏字とかより ppencode
awk って ppencode のようなものって作れるんだっけ!? (括弧は必要だけどね)
以下は、例によって Perl。
#!/usr/bin/perl -w length q bind glob and print chr ord uc qw q sin q and print chr ord uc q exp le and print chr oct oct ord uc q lc eval and print chr oct ord uc qw q gt q

