利用者の陰に潜む、様々な落とし穴 第 2 章 26 レシピ 2.4 sort コマンドの基本と応用とワナ $ cat くく EXSAMPLE ー sort —f ロ > perl ロ > ruby ロ > perl ロ > Ruby ロ > EXSAMPLE ロ Per1 perl Ruby ruby レシピ 2.4 sort コマンドの基本と応用とワナ 問題 UNIX の sort コマンドはいろいろな機能があって強力だときいたが、 うまく使えない。 回答 確かに UNIX の sort コマンドは多機能だ。使いこなせば殆どの要求に応えられるだろう。しかし知らないと ハマるワナがいくつかあるし、またこの質問者は基本からおさらいした方がよさそうだ。そこで、 sort コマンド チュートリアルを行うことにする。何にもオプションを付けずに sort と打ち込むくらいしか知らないというな ら、これを読んで便利に使おう。 基本編 . 各行を単なる 1 つの単語として扱う sort コマンドの使い方には基本と応用がある。基本的な使い方は単純で、各行を 1 つの単語のように見なし てキャラクターコード順に並べるなどの使い方だ。 ー ( オプションなし ) ・・・・・・キャラクターコード順に並べる $ cat くく EXSAMPLE ー sort ロ > perl ロ > ruby ロ > Per1 ロ > Ruby ロ > EXSAMPLE ロ Per1 Ruby perl ruby ←辞書順なので ← P,p,R,r の順で ←並ぶ ・・整数順に並べる ーⅡロ $ cat くく EXSAMPLE ー sort > 2 ロ > 10 ロ > ー 3 ロ > 1 ロ > EXSAMPLE ロ 1 2 10 ←註 ) ←値の小さい順に並ぶ。 もし - Ⅱを付けないと ←ー 3 , 1 , 10 , 2 の順に並ぶことになる ( 2 と 10 の順番が狂ってしまう ) 。 1 一口 ←キャラクターコード順なので ←大文字から先に並ぶ ※マイナス記号は認識するが、プラス記号は認識しない。 ー g ・・・・・・実数順に並べる ( POS Ⅸ非標準 ) ■ $ cat くく EXSAMPLE ー sort —g ロ > + 6.02e + 23 ロ > 1. 602e ー 19 ロ > ー 928.476e ー 26 ロ > EXSAMPLE ロ ー 928.476e ー 26 1 .602e ー 19 + 6 .02e + 23 ■ー f ・・・・辞書順に並べる ←浮動小数点表記でも正しくソートする。 ← - Ⅱオプションと違い、十符号も認識する。
利用者の陰に潜む、様々な落とし穴 32 •sample3. txt 白瀬慧しらせあきら 天野美紗緒あまのみさお 本木紗英もときさえ 第 2 章 このデータを名簿順にソートせよと言われたとする。普通に考えれば、 -k 2 , 2 でいいはずだ。読みがなが第 2 列にあるのだから。ところが日本語ロケール ( LANG = ja ー JP. UTF ー 8 など ) になっている L ⅲ ux 環境で実行する 本木紗英もときさえ 白瀬慧しらせあきら 天野美紗緒あまのみさお $ sort -k 2 , 2 -t samp1e3. txt ロ 具体的には、 sort コマンド引数に一 t ' と追記してやればよい。 る、もしくは -t オプションで区切り文字は半角スペースだと設定しなければならない。 原因は、全角スペースも列区切り文字扱いされているということだ。正しくやるには、環境変数を無効にす 本木紗英もときさえ 天野美紗緒あまのみさお 白瀬慧しらせあきら $ sort —k 2 , 2 samp1e3. txt ロ と失敗する。 いるのでどうやらバグのようだ。バージョンアップまたは GNU 版の使用をお勧めする。ただし、 GNU 版は 確かにヘンな動きをする。 GNU 版ではこの問題は起きないし、最新版 (10. I-RELEASE) では解消されて 回答 seq 1 10 ー sed ' 3 , 4N ; s/%n/-/g' 手元の FreeBSD 環境 (). I-RELEASE) で下記の sed コマンドを実行したのだが、何だか挙動がおかしかった。 問題 レシピ 2.5 sed の N コマンドの動きが何かおかしい →レシピ 1.10 ( プラックリストの 100 件を 1 万件の名簿から除去する ) ・・・ jo ⅲコマンドの話 参考 あるが、それらについて知りたければ、使っている OS の man コマンドで sort について調べてもらいたい。 おめでとう、これでアナタも今日から sort コマンドマスターだ。本当はここで説明していない機能が他にも
sort コマンドの基本と応用とワナ —k 3 , 3 samplel. txt ロ 1995 女あまのみさお 2003 女かわはらさき しらせあきら 1992 女まさきささみじゅらい りようおうき $ sort レシピ 2.4 1992 男 2003 男 1995 男るみや 29 なぜ 2 つ付けるのかについてであるが、入門段階ではとりあえず「そういうもんだ」と思って覚えておけばよ さて次に「名前を降順にソートせよ」という要請を受けたとする。先程は名前をキャラクターコードの昇順に ソートしたが、逆順にしたい場合はどうするか。答えは一 k 3r , 3 である。つまり、最初の数字の直後に基本編で 紹介したオプション文字を付ける。これもとにかくそういうものだと覚えておけばよい。 もし名前が半角アルファベットで記述されていて、それをアルファベット順に並べたかったとする 1995 女あまのみさお 2003 女かわはらさき しらせあきら 女まさきささみじゅらい りようおうき るみや —k 3r , 3 samplel. txt ロ $ sort 2003 1992 1992 1995 男 男 男 ちなみに 今度は「性別→初出年逆順→名前の順でソートせよ」という要請を受けたとしよう。複数のソート条件を指 なら、 -k 3fr,3 と書けばよい。 f は基本編で出てきた「辞書順に並べる」オプションだ。 lnr,l -k 3 , 3 だ。 定する場合にはどうすればいいか。答えは「一 k オプションを複数書く」である。つまりこの場合、 -k 2 , 2 りようおうき るみや しらせあきら 1992 女まさきささみじゅらい 1995 女あまのみさお 2003 女かわはらさき —k 2 , 2 -k 1 Ⅱ r , 1 —k 3 , 3 samplel.txt@ $ sort 1992 1995 男 2003 男 男 性別を昇順にすると「女」が先に来るのは、男女を音読みした場合の名前順で女の方が先だからである。 * 4 どうしても詳しく知りたい人は FreeBSD や Linux の man ページの sort ( 1 ) を見るとよいだろう。 ま
レシピ 2.4 sort コマンドの基本と応用とワナ その 1 半角スペース複数区切りのワナ また新たなサンプルデータファイルを用意する。 •sample2. txt 10 A 31 いる。 ご覧のように第 2 列の位置を揃えるために、 1 行目の文字“ B ”の手前には半角スペースが 2 個挿入されてい る。このような例は、 df 、 ls ー 1 、 ps などのコマンド出力結果や、 fstab などの設定ファイルで身近に溢れて このデータを第 2 列のキャラクターコード順にソートしたらどうなるか ? 1 行目と 2 行目が入れ替わっても らいたいところだが、やってみると入れ替わらないのだ。少なくとも GNU sort 8.4 で実行したらそうなった。 $ sort —k 2 , 2 sampIe2. txt ロ 10 A 理由は、 AWK コマンド等と違い、 sort コマンドは複数の半角スペースを 1 つの列区切りと見なせないから なのだ。 1 行目の 1 つ目と 2 つ目の半角スペースの間にヌル文字から成る第 2 列が存在すると見なしているので ある。 従って列と列を区切る複数のスペースは 1 個にしなければならない。だから、上記のコードは下記のように修 正すれば正しく動く。 $ cat samp1e2. txt > sed 's/[[:blank:]][[:blank:]]*/ /g' ーロ←註 1 ) 連続するスペースを 1 つにする ーロ←註 3 ) 行末のスペースを除去する ーロ←註 2 ) 行頭のスペースを除去する 10 A > sort —k 2 , 2 ロ 注目すべきは苗字と名前の間には全角スペースが入っている点である。 次のサンプルデータを見てもらいたい。これは、第 1 列に人名、第 2 列にかな、という構成の名簿データだ。 ち構えているので注意しなければならない。 ロケールに関する環境変数 (LC_* 、 LANG など ) が設定してある環境で使っている人にはもうーっのワナが待 その 2 全角スペース区切りのワナ 角スペースを入れる場合のあるコマンドで誤動作しないようにするための予防策である。 あと、 2 個目と 3 個目の sed もあった方が安全だ。これは、 ps コマンドのように行頭 ( 第 1 列の手前 ) にも半 まあ当然、位置取りのスペースが消えてガタガタになってしまうのだが・
レシピ 5.27 sort コマンド レシピ 5.28 tac コマンド・ tail コマンド ' -r ”オプションによる逆順出力 . レシピ 5.29test ("[") コマンド レシピ 5.30 tr コマンド レシピ 5.31 trap コマンド 第 6 章レシピを駆使した調理例 あとがき 郵便番号から住所欄を満たすアレをシェルスクリプトで . VII 116 . 116 . 117 . 118 . 118 119 . 119 130
第 5 章 標準入力指定の・ 規表現については、正規表現メモさんの記述 * 24 が便利だろう。 116 どの環境でも使えるシェルスクリプトを書く 多くのコマンドではファイル名として“ー”を指定すると標準入力を意味するのだが、 sed ではこれを使って はならない。 BSD 版の sed は、標準入力ではなく真面目に ' というファイルを開こうとしてエラーになるか らだ。 ロケール →レシピ 5.6 ( ロケール ) を参照 レシピ 5.27 sort コマンド →レシピ 5.6 ( ロケール ) を参照 レシピ 5.28 tac コマンド・ tail コマンド "-r" オプションによる逆順出力 ファイルの行を最後の行から順番に ( 逆順に ) 並べたい時は tac コマンドを使うか、 tail コマンドの -r オプ ションのお世話になりたいところであろう。しかし、どちらも一部の環境でしか使えないし、もちろん POSIX にも載っていない。 ではどうするか・・ 。定番は、 AWK で行番号を行頭に付けて、数値の降順ソートし、最後に行番号をとると いう方法が無難だろう。 ー逆順出力するサンプルコード # 1 # ! /bin/sh # 逆順に並べたいテキストファイル cat くく TEXT > f00. txt cat f00. txt TEXT awk '{print NR,$O}' ー # ←行頭に行番号をつける sort —klnr , 1 ー # ←行番号で降順にソート # ←行番号を除去 sed ' s た [ 0 ー 9 ] * / / , また、ソート対象のテキストデータが標準入力ではなくファイルであることがわかっているのであれば、 マンドを使うという芸当もある。 * 25 * 24 http : //www.kt.rim. or ・ jp/-kbk/regex/regex. htmI#SED 、 25 bsdhack 氏のプログ記事 http : //blog. bsdhack ・ org/index ・ cg 土 / Computer / 20100513. html より引用 ex コ
レシピ 1.9 祝日を取得する •get-holidays. sh # ! /bin/sh # この URL は # Goog1e カレンダーの「カレンダー設定」→「日本の祝日」→「 ICAL 」から取得可能 ( 2014 / 11 / 29 現在 ) 15 Ⅱて 1 = ' https : //www ・ google.com/calendar/ical/j a ・ japanese%23h01iday%40group. v. calendar ・ google.com/pu blic/basic . ics' curl —s "$url" sed —n ' /ABEGIN : VEVENT/ , /AEND : VEVENT/p ' awk ' /ABEGIN : VEVENT/{ て ec 十十 ; match($O,/ADTSTART. *DATE: / ) { s=substr ( $ 0 , RLENGTH + 1) ; match($O,/*SUMMARY:/){ print rec, 1 ,substr($O,RLENGTH + 1) ; 実行例 sort $2==2{print $ 3 ; awk '$2==1{printf ("%d ” , $ 3 ) ; } sort —kln, 1 ー k2 Ⅱ , 2 print rec,2, s ; gsub(/ / # DTSTART 行は日付であるから 「レコード番号” 1 ”日付」にする # SUMMARY 行は名称であるから 「レコード番号 " 2 " 名称」にする = iCaIendar(RFC 5545 ) 形式から日付と名称だけ抽出 # 日付順にソートして出力 # # 1 レコード 1 行にする ー # レコード番号 > 列種別にソート 試しに、平成 26 年度の祝日一覧を求めてみる。 ■実行結果 $ get-holidays. sh ロ 20130101 元日 ( 途中省略 ) 20141123 勤労感謝の日 20151223 天皇誕生日 ( 途中省略 ) 20141223 天皇誕生日 20141124 勤労感謝の日一振替休日 場合は元の日付に加えて振替日も示してくれる。日付だけが欲しくて名称が邪魔な場合は最後の s 。 rt コマンド G 。。 gle カレンダーは、当年とその前後 1 年の祝日一覧を教えてくれる。ご覧のとおり、振替休日が発生する
レシピ 1.10 参照 プラックリストの 100 件を 1 万件の名簿から除去する 17 → RFC 5545 文書 * 9 レシピ 1.10 プラックリストの 100 件を 1 万件の名簿から除去する 問題 員名一覧 (blacklist. txt) がある。会員名簿からプラックリストに登録されている会員のレコードを全て除去 今、約 1 万人の会員名簿 (members. txt) と、諸般の事情によりプラックリスト入りしてしまった約 100 人会 SQL の JOIN 文と同様に考え、それに相当する UNIX コマンドの“扣ⅲ ' を活用する。この場合、「会員名」 回答 blacklist. txt 1 列目 : 会員名 ( 1 列のみのデータ ) members. txt 1 列目 : 会員 ID 、 2 列目 : 会員名 ただし、各々の列構成は次のようになっている。 した、「キレイな会員名簿」を作るにはどうすればいいか。 ■プラックリスト会員を除去するシェルスクリプト (del-blmembers. sh) で外部結合 (OUTER JOIN) し、結合できなかった行だけ残せばよい。 > blacklistl ・ tmp . txt j Oin ー 1 1 ー 2 2 —a 2 —e ' * ' ー 0 1 . 1 , 2.1 , 2 . 2 blackl ist 1 ・ tmp 解説 rm blacklistl . tmp # ! /bin/sh sort —kl , 1 blacklist cat members . txt sort —k2 , 2 # 結合に使う列は予めソートしておかなければならない ( ここでは「会員名」 ) # ・「会員名」でソート # ・ B. L. を右外部紵 ローロロ # ・ null 相当値のある 行だけ抽出 もし join コマンドを知らなかったらどうプログラミングするだろう。恐らくプラックリスト会員を while 文 https : / / t001S. ietf.org/htmI/rfc5545 * 9 こから先は jo ⅲコマンドのチュートリアルを行いながら解説を進めていくことにする。 われれば、外部結合 (OUTERJOIN) を用いるという発想はすぐに出てくると思う。 べースと同様の作業ができるのだ。リレーショナルデータベースを使い、 SQL 文でこの作業をやってよいと言 あまり知られていないかもしれないが UNIX には join コマンドというものがあり、リレーショナルデータ うか。だが、それはあまりにも効率が悪すぎる。 でループを回し、全会員のテキストから 1 行ずっスキャン (grep (v) してくということをするのではないだろ
第 2 章利用者の陰に潜む、様々な落とし穴 $ cat くく EXSAMPLE ー sort —gr ロ ー r ・・・・・・降順に並べる ( 他オプションと併用可 ) ※単純な整数にも使える、計算量が多くなるので、整数には - Ⅱオプションの方がよい。 28 > + 6.02e + 23 ロ > 1.602e ー 19 ロ > ー 928.476e ー 26 ロ > EXSAMPLE + 6 .02e + 23 1 .602e ー 19 ー 928.476e ー 26 ↑註 1 ) 他のオプションと組み合わせて使える ←順番が正反対になっている。 ←先程の - g オプションとは、 ←註 2 ) レシピ 2.3 全角文字に対する正規表現の扱い レシピ 2.3 全角文字に対する正規表現の扱い 問題 正規表現を使って全ての半角英数字 ( [ [ : al Ⅱ : ] ] ) を置換しようとしたら、全角英数字まで置換されてし まった ! 全角文字はそのままにしたいのだが、どうすればいいのか ? 回答 25 $ echo 'MSX MSX2 MSX2 十リ sed 's/[[:alnum:]]/*/g' ロ 象の半角文字を具体的に指定するのでもよい。 いのであれば、それらの環境変数を C ロケールか無効にする。あるいは [A-Za-zO-9] などのようにして置換対 それはロケール系環境変数が日本語の設定になっているためである。従って半角英数字だけを置換対象にした ←ロケールが日本語設定になっているとこうなってしまう 応用編 . 複数の列から構成されるデータを扱う こで紹介する使い方を覚えてこそ発揮される。 SQL の "ORDERBY ”句のよう sort コマンドの本領は、 1995 女あまのみさお 1995 男るみや 2003 男しらせあきら 2003 女かわはらさき 1992 女まさきささみじゅらい 1992 男りようおうき samp ・厄1. txt タ (samplel. txt) があったとしよう。 次のように、キャラクター初出年、性別、 サンプルデータ ( 1 ) ・・・キャラクター名簿 応用編では、 2 つのサンプルデータを例に紹介する。 に、第 1 ソート条件、第 2 ソート条件・・・ 、と指定できるのだ。強力である。 キャラクター名、の 3 列から構成される半角スペース区切りのデー ションを付けてやる。つまり、 -k オプションの後ろにソートしたい列番号をカンマ区切りで 2 つ書く。 無しの sort に渡すだけ ) なのだが、このサンプルデータでは 3 列目にある。こういう時は、 -k 3 , 3 というオプ さてここで「名前順にソートせよ」という要請を受けたとする。名前が 1 列目にあれば簡単 ( 単にオプション だが、それについては「ワナ編」で説明しよう。 ちなみに列と列の間の半角スペースは 1 つでなければならない。 2 つのままだとデータによっては失敗するの $ echo 'MSX * * * M S X 2 $ echo 'MSX * * * M S X 2 $ echo ' MSX * * * M S X 2 解説 MSX2 MSX2 十 LANG=C sed 's/[[:alnum:]]/*/g' ロ← C ロケールにする M S X 2 十 MSX2 MSX2 十 env -i sed 's/[[:alnum:]]/*/g' ロ←環境変数を無効化 M S X 2 十 MS x 2 MSX 2 十 sed 's/[A-Za-z0-9]/*/g' ロ←明確に半角英数字を指定 M S X 2 十 ロケール環境変数 (LC_* や LANG) を認識してくれる GNU 版の grep や sed 、 AWK コマンドの正規表現 は、文字クラス ( [ [ : al Ⅱ : ] ] や C[:b1ank:]] など ) を用いた場合、全角の文字を半角で対応する文字と同一 視する。知っていれば便利だろうが、知らずにそうなってしまった場合は「なんてお節介な ! 」と思いたくなる仕 様であろう。 無効にする方法は簡単なので、回答で示したとおりにやればよい。しかしそもそも、どの環境でも動くコード を目指すために ・意図しない環境変数はシェルスクリプトの冒頭で無効化 文字クラスは使わない →レシピ 5.6 ( ロケール ) 参照 をお勧めする。
20 J0in ー 1 awk ' $ 1 = ↓ 第 1 章 ちょっとうれしいレシピ 1 ー 2 2 —a 2 —e ' * , ー 0 1 . 1 , 2.1 , 2 . 2 blacklistl . tmp j0in ー 1 1 ー 2 2 —v 2 blacklistl . tmp ちなみに、もし SQL の SELECT 文で同じことをするなら、次のように書ける。 SELECT MEM. " 会貝 ID" MEM ”会員名 FROM blacklist AS MEM RIGHT OUTER JOIN members AS BL ON BL. " 会員名 " = MEM " 会員名ー BL. " 会員名 " IS NOT NULL ORDER BY MEM ”会員 ID" ASC; WHERE このようにして SELECT 文でできることは、 jo ⅲを始め、 sed 、 AWK 、 grep などを使えば UNIX コマンドで →レシピ 5.6 ( ロケール ) →レシピ 2.4 (sort コマンドの基本と応用とワナ ) 参照 文字をしつかり定義しておかくこと。 そのような場合は、 export LC ー ALL = C などとして C ロケールにしておくか、一 t オプションを使って区切り いる場合には注意が必要だ。 角空白も列区切り文字として解釈してしまう。例えば人名フィールドがあって姓名が全角スペースで区切られて 利用する場合は一つ注意しなければならない点がある。日本語ロケールになっている場合は、デフォルトで全 join コマンド使用上の注意 はほぼ上から下へ一直線であるのが個人的には好きだ。 句 ) → WHERE 句→ ORDERBY 句→ ( 最初に戻って )SELECT の直後、であるが、シェルスクリプトの場合 も大抵できる。ついでに言うと、 SELECT 文でデータの流れを追えば、 FROM 句→ (RIGHT OUTER J()IN