名前のとおり、ファイルの有無を返すコマンドだ。「それだけ?」と思うかもしれないがそれだけだ。ただし、標準出力から有無を調べたいファイルを一覧として受け取れるようなっている。
1 2 3 4 5 6 7 8 9 |
> exist /etc/resolv.conf 1 > exist /etc/no_such_file 0 > echo "/etc/resolv.conf" > filelist.txt > echo "/etc/no_such_file" >> filelist.txt > exist -display < filelist.txt /etc/resolv.conf > |
ファイル名を引数で直接指定すればそのファイルの有無を1(=ある)または0(=なし)で返してくれる。一方”-display” というオプションを付け、有無を調べたいファイルリストを標準入力から渡せば、存在しないファイルがフィルタリングされ、存在するファイルのみなって標準出力される。(”-display2″ オプションにすると動作が反転する)
残念ではあるが長いため割愛させていただく。しかしながら、中身はC 言語で書かれている。道具になれなかった理由とはファイルの有無を確認したいなら、わざわざ新しいコマン
ド覚えて使わずともシェルスクリプトのファイル演算子(”-f”や”-e”)を使えばいいと誰でも想像が付く。それゆえに廃れていったのだろう。
「そんな未来が容易に想像できるにも関わらず、何故わざわざこんなコマンドを作るという愚行に出たんだ!」そう思うかもしれない。しかし実は、擁護すべきある一つの意図があった。
複数あるファイルの有無確認を、シェルスクリプトや標準のUnix コマンドの範囲で行おうとするとループ構文が必要になる。*1 しかし、これはパイプで処理をこなす合理性を追
求するユニケージ開発手法からすると避けたい手段だったのだ。ゆえに、このコマンドに関しては今でもこのために要・不要論が巻き起こるのだという。
*1 実は“xargs ls -1 2>/dev/null” などとやれば出来なくはないのだが、内部的にエラーが起こり、戻り値も0 にはならない。従って最善の方法とは言い難いのだ。
語源は、“select field and uniq” だという。つまり、今のTukubai のself コマンド+ uniq コマンドを合わせたようなコマンドである。こうして「合わせたようなコマンド」と書かれているあたりで、廃れていった理由は自明に思えるだろうが、中身を見ていただきたい。
基本的な作業はuniq コマンドと同様「行の集約」であるが、uniq コマンドは行頭から行末まで全て一致していないと集約しないのに対し、seniq は指定されてフィールド番号のフィールドさえ全て一致していれば集約する。
従って上記の動作例の場合、1 ~ 3 行目は引数で指定された第1、第3 フィールドがいずれも一致しているので(最初の行に)集約されたが、最後の4 行目は第3 フィールドが一致しておらず、この行だけ集約されずに出力されたというわけだ。
リスト3に全体を掲載する。珍しいことにC シェルで書かれている。*1
*1 本誌はシェルスクリプトをメインに扱う雑誌でありながら、C シェルによるソースコードが登場するのは何とこれが初めてである!
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 |
#!/bin/csh -ef # # seniq >>> 指定フィールドのみでuniq処理を行う。 # (seniq >> select field and uniq) # # Usage : seniq f1 f2 ... < infile > outfile # # Written by N.Tounaka (usp-lab) #プロセスidの取得 set tmp = $$ #作業ディレクトリの設定 set d = /tmp #コマンド書式の表示 if($#argv == 0) then echo2 "Usage : seniq f1 f2 ... < infile > outfile" exit 1 endif #入力ファイルの同定 if(! -e $argv[$#argv]) then cat - > $d/$tmp-01 set file = $d/$tmp-01 set dif = 0 goto NEXT endif set m = 1 while(1) if(! -e $argv[$m]) then @ m ++ else set file = "$argv[$m-$#argv]" @ dif = $#argv - $m @ dif ++ break endif end NEXT: #ファイルの存在チェック if(! -e $file) then echo2 "Error : $file ファイルがありません" exit 1 endif @ n = $#argv - $dif #セニーク編集をするAWKスクリプト cat << FIN > $d/$tmp-02 NR==1 { a="$argv[1-$n]"; n=split(a,x," ") m=1 for(i=1;i<=n;i++) { if(x[i]~/\//) { start=transnumber(substr(x[i],1,index(x[i],"/")-1)) last =transnumber(substr(x[i],index(x[i],"/")+1)) for(k=start;k<=last;k++) { y[m++]=k } } else y[m++]=transnumber(x[i]) } m-- ore=nre=\$0; old=new=makerecord() } { new=makerecord() if(old != new) { print ore; old=new; ore=\$0 } } END { if(flg==1) exit; print ore } function makerecord() { s=""; for(i=1;i<=NF;i++) { for(j=1;j<=m;j++) if(i==y[j]) { s=s " " \$i;break } } return substr(s,2) } function transnumber(q) { gsub("NF",NF,q) pp=index(q,"-") if(pp != 0) q=substr(q,1,pp-1)-substr(q,pp+1) return q+0 } FIN #実際の編集 awk -f $d/$tmp-02 $file #一時ファイルの除去 #rm $d/$tmp-* #正常終了 exit 0 |
こんな特殊な働きをするコマンド、一体どんな用途を想定して作ったというのだろうか。それは、例えばこういう用途である。
銘柄 日付 時刻 株価
という4 フィールドから構成された株式チャート表がある。銘柄・日時毎に取引開始から終了まで1 分刻みで株価が記録されている。この表から各銘柄の
毎日の始値を抽出してもらいたい。この時、seniq 1 2 とすれば、銘柄・日付毎の始値を抽出できるというわけだ。
このように、ある項目の先頭行だけを取り出してもらいたいという需要はよくあることである。しかしながら、これを既存Unix コマンドやその簡単な組み合わせで実現することは難しく、そうした経緯で生み出されたコマンドだったのだ。
seniq 自体は淘汰されたが、同様のものが“getfirst” という名前のTukubai コマンドとなってリリースされた。「名が体を成していない」ために名前が変えられたということだ。確かにgetfirst という名の方が直感性に優れている。
そして、兄弟コマンドとして“getlast” もリリースされた。また一致を確かめたいフィールドの指定方法も、個々に列挙するのではなく範囲指定に変更された。これは前章で述べたsm2 の書式の決まり方と同様の理由であろう。
その4.sm3 コマンド
Tukubai コマンド一覧を見ると、sm2 の次がsm4、sm5になっており「sm1 やsm3 は何処へ行った!?」と思うことだろう。そう、それら欠番コマンドは道具になれずに淘汰されていったコマンドなのだ。
sm3 は、ソートせずにsum-up を行うコマンドであった。これもソースファイルが長過ぎて掲載できないのだが、機能だけでも是非紹介しておきたい。
使い方と動作例
例えば2012 年日本シリーズのスコアがイニング毎に記録されたファイル(NipponSeries2012.txt) があったとしよう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
> cat NipponSeries2012.txt 第1戦 1回表 日ハム 0 第1戦 1回裏 巨人 0 第1戦 2回表 日ハム 0 第1戦 2回裏 巨人 0 : 第2戦 1回表 日ハム 0 第2戦 1回裏 巨人 1 : : 第5戦 2回表 巨人 2 第5戦 2回裏 日ハム 1 第5戦 3回表 巨人 3 第5戦 3回裏 日ハム 1 : : 第6戦 9回表 日ハム 0 第6戦 9回裏 巨人 0 > |
この表から1 戦毎のスコア合計を求めたい場合、sm3 があれば次のようにして一発で求めることができる。
1 2 3 4 5 6 7 8 9 |
> sm3 1 1 3 3 4 4 NipponSeries2012.txt 第1戦 巨人 8 第1戦 日ハム 1 第2戦 巨人 1 第2戦 日ハム 0 : 第6戦 巨人 4 第6戦 日ハム 3 > |
現在リリースされているTukubai コマンドの範囲でやるなら、delf でイニングのフィールドを消し、更に試合番号とチーム名フィールドでソートしてからsm2 に掛ける必要がある。
1 2 3 4 5 6 7 8 9 10 |
> delf 2 NipponSeries2012.txt | sort -k1,2 | sm2 1 2 3 3 第1戦 巨人 8 第1戦 日ハム 1 第2戦 巨人 1 第2戦 日ハム 0 : 第6戦 巨人 4 第6戦 日ハム 3 > |
上記の代替例で示したとおり、簡単な記述で他のコマンドに置き換えられたからだ。このように単機能でないコマンドはよほど頻繁に用いられない限り、淘汰される運命にある。
ちなみに、もう一つの欠番コマンドである sm1 について軽く紹介しておくと、これは横方向の sum-up を行うためのものだったという。これは先程のseniq コマンドと同様の運命を辿り、ysum(横サム)コマンドとなって現在に至る。