起こりうるエラーは必ず起こる

テストの主な利点は、テストを実行している時ではなく、テストについて考え、テストを作り出している時に生み出される

David Thomas, Andrew Hunt『達人プログラマー 第2版』村上雅章(訳)

今回扱うのは私のプログラミングでの失敗談だ。
最も重要な結論はタイトルの通りだが、いくつか過程で得られた知見もあったので備忘録としてまとめておく。

シェルスクリプト不都合な真実

シェルスクリプトはさっと書いて動かせる便利なスクリプト言語だが、「不都合な真実」とでもいうべき仕様がちらほらと散見される*1。今回ハマったのはif文の分岐処理だ。

まず、下の簡単なif文についてみてみよう。

#!/bin/bash
FLG1="true"
if  "$FLG1" ; then
	echo "FLG1: TRUE"
fi
FLG2="false"
if  "$FLG2" ; then
	echo "FLG2: TRUE"
fi

これの出力結果は以下のようになる

FLG1: TRUE

世にあるその他のプログラミング言語と共通の、一般的なbooleanによるifの分岐に見える。
では次はどうだろうか。

#!/bin/bash
FLG1="true"
if  [ "$FLG1" ] ; then
	echo "FLG1: TRUE"
fi
FLG2="false"
if [ "$FLG2" ] ; then
	echo "FLG2: TRUE"
fi

これを実行するとこうなる。

FLG1: TRUE
FLG2: TRUE

両方出力されてしまった。

まず、挙動が異なる原因は、testコマンドの挙動によるものである。シェルスクリプトでは「if []」は「if test」に解釈される。
では、「test true」も「test false」も同じ結果になったのは何故だろうか。これはどちらのコマンドも0の終了ステータスを返すからである。

The test-commands list is executed, and if its return status is zero, the consequent-commands list is executed
Bash Reference Manual

というわけで、最初の例を見て条件式のtrue, falseで分岐したと思っていたのが実は間違いだったということになる。bashには実はtrue, falseというコマンドがあり、それぞれ終了ステータスとして0, 1を返したためそのように見えただけだったのだ*2

エラーとif文のコンボ

これを踏まえて本題に入ろう。私がシェルで今回実装しようとしたのは、何かコマンドを打って、得られた結果から特定の情報を取得したい場面であった。
例えば、curlを打って返ってきたresponseのデータや、データを出力したテキストファイルの中身などをコマンドで取得し、その結果がCMD変数に格納されたとする。CMD変数内の情報を得て、それに応じて処理を変えようとしたのが以下のコードだ。

#!/bin/bash
# success: trueなら成功
if [ $(cat $CMD | grep "success:" | awk '{print $2}') != "true" ] ; then
        echo "失敗"
	# 失敗時の処理
else
	# 成功時の処理
fi

これでコードを走らせたところ、しばらくうまくいっていたのだが、ある日後続処理がうまくいっていないことに気づいた。
しかし、ログを見ても「失敗」のメッセージは出ていない。一体なぜ…と思っていたら、調べてみるとまず失敗しているのはCMDに結果を格納するコマンドであることが判明した。

$(cat $CMD | grep "success:" | awk '{print $2}') != "true" # <-これがエラー

というわけで、ifの条件式部のコマンドでエラーが出たため終了ステータスとして失敗を表す1が返され、elseの方の処理が走ったということであった。これを踏まえると、実は下のようにコードを変えると、元々やりたかった処理を実現しながら、CMD変数部分のエラーもキャッチできるようになることがわかる*3

#!/bin/bash
# success: trueなら成功
if [ $(cat $CMD | grep "success:" | awk '{print $2}') = "true" ] ; then
	# 成功時の処理
else
        echo "失敗"
	# 失敗時の処理
fi

コード部だけ見れば全く同じ処理をしているように見えるのだが、不思議なものである。

それは本当に失敗しないか?

これが今回私が遭遇したエラーであり、直接の原因としてはシェルスクリプトのifの仕様を詳しく把握していなかったことにあるのだが、重要なのはどうして最初に実装した時に気付けなかったかだろう。

後から考えてみれば、今回必要だったのは、例えばどんなデータがここに入ってくるのか、この処理が失敗したらどうなるかというテスト駆動的思考だったのではないかと思う。全ての処理は失敗、あるいは想定外の値が入りうる、とまではいかなくても、想定したフローの中でどういうことが起こりうるのか、そして、起こりうることは必ず起こると想定してエラー処理などを組むべきであった。

そういえば大学の頃にバグを見つけるのが異様にうまかった友人がいたが、今度氏に会った時にどうやってバグを見つけているのか聞いてみても良いかもしれない。バグを知り仕様を知れば百戦危うからず…とまではいかないかも知れないが。

*1:もちろんこれは私のわがままである。インターネットでもシェルスクリプトJavaScriptの「おかしな」仕様について面白おかしく囃し立てる人々がいるが、未定義動作とバグ以外は仕様は仕様でしかない。ある値を渡して何を結果として返して欲しいかは人と場合によるとしか言うことはできないし、人のイメージや直感ほど当てにならないものはないだろう。

*2:これはシェルにおいて0が成功を、1が失敗を表すステータスであるため、整合性をとるためにそうなっていると考えられる。

*3:もちろん、それぞれ違うタイプのエラーなので失敗時の処理にCMD変数を表示するなどのエラー調査をやりやすくする工夫は必要。

フェデラー、去る

2022年9月15日、ロジャー・フェデラーが引退を発表した。生涯グランドスラム達成、通算グランドスラム獲得数20勝の偉大な選手がコートを去る。

 

私にとってフェデラーは常に憧れの選手であった。私が現役でテニスをやっていた頃、彼はそれまでのテニスの歴史から言えばすでに引退を考え始めても良い年齢だった。しかし、その歳にあっても彼のプレーは当時中学生の頃の私の夢中にさせるものであった。完成されたサーブ、正確さと力をともに備えたフォアハンド、インぎりぎりをつく鋭いバックハンド、相手の意表をつくドロップ、そして何よりプレー全てが美しかった。

 

私が実際にテニスを見始める少し前からテニスは彼を含むいわゆる「ビッグ4」と呼ばれる選手たちが支配していた。ラファエル・ナダル、ノヴァク・ジョコビッチアンディ・マレー…そして、ロジャー・フェデラー。ここに挙げられた選手たちは全員違う時期に生まれていればその時代のテニスの絶対王者になっていただろうとも称されるプレイヤーであり、彼らの試合を生で見られる時代に生まれ育ったのはテニス視聴者としては最高にラッキーだろう。深夜まで起きて彼らのグランドスラム決勝の試合をテレビにかじりついて見ていた。特に、フェデラーが出ている試合は。

 

フェデラーはよく「最強の選手」、「最高のプレイヤー」と呼ばれていたし、私もそれを信じていた。今のテニスを見ている人たちからすれば、少し事情が異なるかもしれない。私が試合を見ていた当時彼が大量に保持していた「グランドスラム通算勝利数」や「世界ランク1位在位期間」のような記録の多くはナダルジョコビッチに塗り替えられてしまったし、彼らとの通算成績も負け越している。年齢や怪我を考慮しても、彼らがいまだにコートに残り、新世代の選手たちとプレイしていまだにトップクラスにいる状況を見れば、そうだったのだろうと冷静に考えられるようになった今なら思える(正しく「熱狂的」ファンであった当時には受け入れ難かったことかもしれないが)。

 

だが、彼の偉大さは決してプレーの中にだけあるわけではないし、むしろ今のテニスの世界を作り上げた部分によるところが大きいのではないかと思うのである。

 

全てのスポーツの世界と同様に、テニスの世界でも王者という存在は現れては消えていく。それは、王者というものはその牙城が崩されることが前提の概念だからだ。テニスのそれぞれの時代には必ず始まりと終わりがある。プレイヤーはその時代の目撃者に熱狂を持って迎えられ、その時代を作り上げ、そして次の王者に時代を明け渡す。前時代の王者は時代の移り変わりと同時にコートを去っていくーーー時にはその目撃者とともに。新しい時代を作り、そして次の時代へ橋渡しをする、それこそが王者の持つ悲しいと同時にもっとも素晴らしい役目なのだろう。

 

フェデラーは若干21歳で当時の絶対的王者であったサンプラスを破り、次の時代への転換を告げた。彼はその後数年はグランドスラムを取れない時代が続いたが、サンプラスの次の時代を切り開いたのは彼とその同年代の人々だと言えるだろう。その後数年たち、彼は全てのテニスプレイヤーの悲願であるグランドスラムを取り、年間勝率9割を叩き出して王者となった。彼の王者の時代はかなり長くなると当時見ていた人たちは思っただろう。それほど圧倒的な強さだったのだから。

 

ところが、若手として全仏オープンの門番ラファエル・ナダルや後に「ビッグ4最強」とも言われるようになるノヴァク・ジョコビッチが現れてくると、絶対的な王者という存在はいなくなった。数年はフェデラーの優位が続いたが、その後はビッグ4がグランドスラムを分け合うような形でテニスはビッグ4に支配される長い時代に突入することになった。これが私が学生時代に見ていた頃である。

 

彼が他の時代の王者と異なっていたのは、次の時代に対して常に対応し、王者の座に着くことまではできなくとも、誰も王座に定着できないような時代にしたことであろう。次の世代に対し、図々しくも前時代の人間が舞台に居残りを決め込んだわけである。もちろん、これはできればどの時代の選手たちもやろうとしたことであり、しかし、誰もできなかったことなのである。何故なら、次の時代の選手たちは、今の時代の選手たちを打倒するべく対策を積み、前時代のものたちを放逐するために参戦してきたのだから。しかし、フェデラーはその驚異的な対応能力で次の時代にも居座ってしまったのだ。

 

面白いのは、これが必ずしも能力によるものだけでなく、むしろ彼の性格に依拠する部分が多かったと思われるところだ。彼がビッグ4時代以前の絶対的王者として築き上げた成績はあまりに輝かしく、もう自分は一生涯の仕事をしたと思ってもなんら不思議ではない。実際に、おそらくそこでキャリアを引退していたとしても歴史的名選手として語り継がれたことだろう。しかし、彼は飽くことはなかったのである。キャリアグランドスラムをとっても、通算1000勝しても、すでにその名声はテニス界だけに留まらなくなっていても、それでも彼はテニスが好きであり、プレーすることが喜びであり、勝つことに貪欲だったのである。モチベーションを保つということは想像するよりも遥かに難しい*1ことを鑑みると、ある意味彼のもっとも優れた部分は、彼のテニスへの情熱はキャリアをスタートしてから先日の引退会見まで衰えることがなかったというところだろう。

 

スポーツでは年齢は基本的には抗えない絶対的なハンディキャップだが、日々のトレーニングやプレースタイル、戦略の取りようによって十分戦えるようになることを示したのも彼だった。彼以前には選手のキャリアは20代中盤ごろがピークであることが多かったが、フェデラーが30代になっても驚異的な成績を叩き出すことに刺激されてか、30代でキャリアハイを更新する選手も現れるようになった。

 

怪我や加齢によってランキングや成績を落とすと必ず「フェデラーは終わった」と囁かれたが、その度に劇的な復活劇を見せてきた。当時ともに「落ち目」だと言われていたナダルとの壮絶な決勝を経て優勝を果たした2017年の全豪オープンはリアルタイムで見て最も感動した試合だ。この年は自身が最も得意とするウィンブルドンでも8度目の優勝を達成した。ちなみにこの記録はいまだに破られていない。

 

私を含む多くの人々がフェデラーを「最も偉大な選手」と評するのはこのような部分によるところが大きいのではないだろうか。肉体的なピークを迎えても、自分のプレーを徹底的に対策してきた「自分を倒すためにやってきた選手」にやられても、怪我で長期間プレーできなくなっても、その度彼はその時取れる最善の対策を打ってきた。必死に時代についていこうとし、その結果実は彼の方がリードしているような状況になることも何度もあった。歴史上多くの王者が一つの時代を築き上げてきたのに対し、フェデラーは常に新しい時代を創造し続け、そしてそれらの時代と共にあろうとしてきた。この姿勢こそが、彼に多くの人々が惹きつけられてやまない理由なのではないかと思う。

 

王者であろうとなかろうと、全ての選手に終わりは来る。ここ数年間に渡る怪我との戦いも、彼としては絶対に勝ってコートに戻るつもりだったはずである。しかし、彼のキャリアにもとうとう終わりが来た。世界中で彼の引退に対する悲しみの声と、このような素晴らしいテニスの時代を作り上げた偉大な選手として称える声があがっている。これも当然のことだが、あえて私は、歴史上最もテニスを愛したとんでもなく負けず嫌いな愛すべき選手として彼の引退を惜しみたい。

 

*1:キャリアグランドスラムを達成した後の一時期のジョコビッチを見ればたとえプロの選手といえど(むしろ、プロの選手だからこそかもしれないが)、ずっとテニスをプレーしたいという気持ちであり続けることは非常に困難であることがよくわかる

日記、再び

ずっと停止していたブログを再開することにした。

また三日坊主で終わるかもしれないが、まあそれでも良いだろう。

 

一応近況報告をすると、1年前に状況が大きく変わった。

まあ言ってしまうと社会人になったのだが、それできっちりとした人間になれたかというと、そんな甘いことはなく、むしろ学生の頃よりもひどく自堕落になってしまったような気さえする。

 

1年足らずではあるが、今の状況や自分の意識をなんとかしようということで、また色々と自己変革に乗り出しており、これもその一環というわけである。

とはいえ日記じみたことは苦手であることはこの数十年生きてきてようやくわかり始めたので、しばらくは小説なり音楽なり自分の趣味のことについての記事を書く予定である。