難しいものを簡単にする方法
(jvns.ca)- Julia EvansのStrange Loopキーノートは、DNS、Bash、HTTP、SQLのように「基本」に見えるのに習得に時間がかかる技術がなぜ難しいのか、そして 学習のハードルを下げる方法 を扱う
- Bashが難しいのは、
set -eが||条件内の関数呼び出しで無効化されるような 小さな例外や落とし穴 が多く、多くの人がBashをたまにしか使わないため正確に覚えにくいからである - HTTPとSQLは、単純な表面の裏に ブラウザの2,000万行規模の実装、多数のヘッダーやフラグ、SQLの記述順と実行順の違いが隠れており、学習負荷が大きい
- DNSは、ライブラリ、キャッシュ、権威ネームサーバーとの通信がユーザーから見えにくく、
digの出力も複雑なため、隠れた動作を可視化するツール とデモが重要である - 良い学習支援とは、ツールやリファレンスを共有し、大きなリストを実際に使う小さなリストに絞り、コンピューターがしていることを時系列で説明し、失敗談やバグ記録まで一緒に共有することである
「基本」に見える技術に時間がかかる理由
- Strange Loopキーノート Making Hard Things Easy は、学びにくい技術をより簡単にする方法を扱っている
- 出発点はDNSだった
- ドメイン名のIPアドレスを見つける作業は単純に見えるが、発表者はDNSを学び始めて7年経ってもWebサイト設定中に問題に遭遇し、全体としては約10年かかったと話している
- 友人たちも同じ問題を繰り返し経験しており、多くの人が「もう理解していて当然なのに」と個人の問題のように受け止めてしまっていた
- 発表者はこうしたテーマをわかりやすく解説するために Wizard Zines という小さな出版社を始め、Bash、HTTP、SQL、DNSを題材にしている
Bash: 覚えにくい例外はツールに任せるべき
- Bashはプログラミング言語だが、発表者が使う言語の中でも 奇妙な挙動 が多いほうである
- 例のスクリプトでは、
mv ./*.txt /tmmppが失敗してもBashはデフォルトでは停止せず、echo "success!"を実行するset -eを使えば失敗時に停止させられる- しかし関数を
f || echo "failed!"のように||条件の中で呼ぶと、関数内ではset -eがグローバルに無効化され、再びsuccessが出力される - この挙動はBashのバグではなく、文書化された仕様である
- Bashが難しい理由の1つは、多くの人が 半年に1回 くらいしかBashスクリプトを書かず、その後また見直さないことにある
- たまにしか使わないシステムが大量の雑多な知識と落とし穴で埋まっていると、正しく使うのは難しい
- 「Bashは誰にも使えない」という反応は事実ではない
- 多くの人がBashを使っていて、完璧ではなくても実際によく仕事を片付けている
- 目標は、圧倒的な落とし穴の山の前に立っている人を「だいたい正しく使える状態」へ移すことだ
- ShellCheck は、人が覚えにくいBashの落とし穴を代わりに記憶して警告してくれるツールである
shellcheck -o all bad-again.shは、SC2310警告で||条件内で呼ばれた関数ではset -eが無効化されると知らせてくれる- このチェックは
-o allで実行しないと表示されない - こうしたツールは雑多な知識をコンピューターに任せ、認知負荷を減らしてくれる
失敗談は「ベストプラクティス」より判断に役立つ
- ツールを自分で作らなくても、すでに使っている便利なツールを友人や同僚に知らせることが重要である
- 発表者もShellCheckを知ったのは遅く、その間ずっとすべてを頭の中で覚えておく必要はなかったのだと知って腹が立ったと話している
- 落とし穴や失敗談の共有は コミュニティへの貢献 に近い
- Bashの
set -e無効化の例は、数週間前に友人のJesseから聞いた体験談から学んだものだった - 他人の失敗談を知れば、自分で体験しなくても同じ問題を避けられる
- Bashの
- 「誰もBashを使うべきではない」のような強い意見より、Bashが実際にどんな問題を起こしたのかという話のほうが有用である
- 同じ話を聞いて、ある人はShellCheckを使いながら簡単なBashスクリプトを維持しようと決めるかもしれない
- 別の人はBashをそもそも使いたくないと判断するかもしれない
- 同じ事例に対する反応が違っていても問題ない
HTTP: 2,000万行のブラウザを前提に理解しなければならない
- HTTPレスポンスは、ステータスコード、ヘッダー、本文という単純な構造に見えるかもしれない
- しかし「なぜヘッダーを設定しなければならないのか」という問いは、すぐに ブラウザの動作 へつながる
- Firefoxは約2,000万行のコードでできている
- ブラウザは1990年代から進化してきており、セキュリティモデルもWebの攻撃や変化に合わせて更新され続けてきた
- ある話題が難しい理由を理解するには、その背後に非常に大きなコードベースがあるかどうかを見る必要がある
- HTTPそのものだけでなくCSSやJSなども含まれるが、現代ブラウザの複雑さはHTTPの学習障壁を説明する助けになる
- 大きなリストは 小さなリスト に絞ったほうが理解しやすい
- HTTPリクエストヘッダーの一覧は43個以上あり、非公式ヘッダーも存在する
- 発表者は HTTP request headers comic で、自分が知っていて使う15個のヘッダーを扱っている
- 「最も重要なヘッダー」は客観的な一覧ではなく、自分が知っていて使っている主観的な一覧である
- たとえば
Accept-Encodingをgzipに設定すれば圧縮レスポンスを受け取れる、程度を知っていれば普通は十分である
- コマンドラインツールにも同じやり方を適用できる
grepのman pageには多くのフラグがあるが、発表者は20年間grepを使っていても全部は把握していない- 経験者が「私はこのシステムで7つだけ知っていて、それはこれです」と言ってくれると初心者の助けになる
- 別の経験者はまた別の7つを知っているかもしれない
リファレンスは実際に使うものを正直に共有すべき
- 人の頭に入りきらない情報には、良い リファレンス が必要である
- 発表者はCSSを20年間断続的に学んできたが、CSS-Tricks を知ったのはここ2年ほどのことで、もっと早く知っていれば助かっただろうと話している
- CSS-Tricksは買収後、4月から新規記事の公開が止まったようだが、既存の記事は今でも有用だと見ている
- HTTPではMozilla Developer Networkをよく使っている
- HTTPの公式リファレンスとしては、2022年に書かれた RFC 9110、9111、9112、9113、9114 がある
Connectionヘッダーの正確な動作のような詳細を調べられる- 発表者の主なリファレンスはたいていMDNだが、公式RFCがよく整理されている点を高く評価している
- リファレンスを共有するときは、格好よく見えるから共有するものと、実務で本当に使っているものを区別すべきである
- 実際にはw3schoolsのような「それほど格好よく見えない」リファレンスを使っているとしても、実際に使っているかどうかを正直に言うことが重要である
SQL: コンピューターがしていることを時系列で話す
- SQLはクエリを書く順序と、実際の概念的な実行順序が異なるため、初学者には混乱しやすい
- 発表者が使っているSQLのメンタルモデルは次の順序である
FROMWHEREGROUP BYHAVINGSELECTORDER BYLIMIT
- 現実のデータベースには最適化があるため、実際はもっと複雑だが、この 時系列モデル は大半の状況で役立つ
- クエリに書かれた順序とほぼ同じだが、
SELECTが5番目である点が違う
- クエリに書かれた順序とほぼ同じだが、
- 「コンピューターは実際に最初に何をするのか」を問うやり方は、ほかの話題にも使える
- CORSでは、ブラウザとサーバーの間にあるすべての通信を時系列で書き出して理解できる
- 発表者は CORS comic をこの方法の例として挙げている
- 時系列での説明は単純そうに見えて、実際には難しく、だからこそ協業に役立つ
- Behind Hello World on Linux は、Linuxで
hello worldを実行したときに起きていることを扱っている - 発表者は10年前にも 似た記事 を書いたが、2023年の記事は約6倍の長さになった
- Linuxがより複雑になったというより、2013年当時は自分が時系列で何が起きているかを今ほど理解していなかったのだと考えている
- Behind Hello World on Linux は、Linuxで
- チームでも、APIエンドポイントにリクエストが来たときに何が起きるかを時系列のタイムラインとして一緒に作れば、それぞれが知っている部分をつなぎ合わせられる
DNS: 隠れたシステムを見せることで直感が育つ
- DNSは、ブラウザ、DNSリクエストを送るライブラリ関数、キャッシュ、権威ネームサーバーが一緒に動く仕組みである
- 問題は、その多くがユーザーから 隠れている ことにある
- どのライブラリコードがDNSリクエストを送っているのかを突き止めるのは簡単ではない
- キャッシュは内部に保存されたデータを簡単に調べにくく、ユーザーも制御できない
- キャッシュと権威ネームサーバーの間の通信も見えない
- 発表者は友人のMarieと一緒に Mess With DNS という小さなDNSサーバーを作った
- ユーザーはドメインにDNSレコードを作成できる
- リゾルバーからリクエストが来るたびに、どんなメッセージが届いたかを表示する
- Strange Loopのデモでは、
strangeloopという名前にorange.jvns.caを指すCNAMEレコードを作り、ブラウザが使うカナダのDNSリゾルバーがAレコードとAAAAレコードを要求することを確認した
- 隠れたものを見せる別の例として float.exposed がある
- 32ビット浮動小数点数でsignificandとexponentを変えながら、次の浮動小数点数や間隔の変化を見られる
- DNSが難しいもう1つの理由は、巨大な分散システムだという点にある
- 発表者は「500万台以上のコンピューターが関わりうる」といった感じで、その大半をユーザーは制御できず、一部は期待どおりに動かないこともあると話している
digの出力のように、ツール自体が学習障壁になるとき
- DNSツールの出力も混乱を大きくしうる
digには+norecurseフラグがある- リゾルバーに、すでにキャッシュにある結果だけを返すよう要求できる
dig +norecurse jvns.caは、そのリゾルバーが直近5分ほどの間にそのドメインをキャッシュしていたかを確認するような使い方ができる
digの出力は、初心者にDNS自体がさらに複雑なのだという印象を与えかねない- 発表者は、これは1990年代に決まった比較的恣意的な出力形式が長く維持されてきた結果に近いと見ている
- 「eraser eyes」とは、複雑な出力の中で実際に見る部分だけを残し、残りは消しゴムで消すように無視するやり方である
- 例では
SERVFAILレスポンスコードだけに注目している - 発表者の理解では、この文脈での
SERVFAILは「キャッシュにない」に近い意味である
- 例では
- ツールをデモするとき、どの出力やUIを見て、どの部分を無視しているのかを伝えると学習の助けになる
digは出力が荒削りだが機能が多く、+norecurseをサポートし、どこにでもあり、長く変わっていないという安定性が長所である
一緒に簡単にしていくための役割
- 技術を簡単にする実践は、ブログがなくても身近な人たちと共有できる
- 発表者がまとめた方法は次のとおりである
- 役に立つ ツール を共有する
- 実際に使っているリファレンスを共有する
- コンピューターで起きていることを時系列で話す
- 大きなリストを、自分が実際に使う小さなリストに絞る
- 隠れた動作を見せる
- 混乱しやすいツールをデモしながら、どこを見ているかを伝える
- 助け方にもさまざまなタイプがある
- 「文句の多い古参ユーザー」は、過去に何がまずかったかを話して苦労を減らしてくれる
- 「声の大きい初心者」は、「これはどう動くんですか?」と質問して、ほかの人も安心させる
- シニア開発者が知らないことを公の場で質問すれば、無知だと思われるのを心配している人も一緒に学べる
- 「バグ記録係」は、同じバグが再発しないよう何が起きたかをまとめる
- 「ツール制作者」は、繰り返し説明する代わりにコードを書いて問題を恒久的に簡単にする
- 「今日学んだこと共有者」は、新しいツール、遭遇したバグ、新たに知ったライブラリ機能を共有する
- 「700個のタブを開いている人」は、どこで情報を探せばよいかをすでに知っている可能性が高い
- 「質問に答える人」や「あとで見つけられるよう書き残す人」も必要である
- 基本に見えるものを難しく感じるのは、個人だけの問題ではない
- 多くの人が同じ理由で同じ地点でつまずく
- 難しさの理由を把握できれば、コンピュータープログラムのバグを直すように、よりうまく対処できる
- 難しさを生む要因には、膨大な雑多な知識と落とし穴、2,000万行規模のコード、隠れたシステム、改善されていないわかりにくいツール出力などがある
- 発表者は、Gitがなぜ難しいのかはまだよく理解できていないが、引き続き考えて見極めたいテーマとして残している
1件のコメント
Hacker News の意見
いちばん心に響いたのは、普段は隠れているものを見せよという言葉だった
こうしたツールは、ほとんど即座に状況をより明確にしてくれる。Web ブラウザの開発者ツールを考えてみると、そういうものがなかった「暗黒時代」には、何が起きているのか見えず推測しなければならず、ひどいものだった
Wireshark のように、アクセス可能なネットワークパケットのバイト列を見せ、構造までパースしてくれるツールは、ネットワークのデバッグだけでなく、何も隠さないのでネットワークの概念を教える上でも非常に役立つ
オープンソースソフトウェアもその点が好きだ。バグの原因を理解したり、ドキュメントが残した知識の空白を埋めたり、プログラミングの概念をさらに学んだりするためにソースを見られるので、何も隠されていない
たとえば Ethernet のプリアンブルは決して表示しないし、Ethernet フレームのチェックサムはたまにしか表示しない。Ethernet プロトコルの必須要素であるフレーム間ギャップも決して表示しない
かなり近いところまでは行くが、どこかには常にさらに多くの詳細が隠れていることを示している
すでに頭の中では可視化しているし、コンピューティングに関するどんな説明も結局は図で表される。なのにコーディングするときには図がまったくない
すべてのコードを動的に計装して、GUI にメッセージを送ればいい
その知識を持ち、教えることができた専門家たちも、今では組織の内部ではなく、そうしたツール会社に集まっている
だからコマンドラインへ自然に移っても、すぐ慣れて直接使える
Julia はテック業界で最も好感の持てる人物の一人のように思う
文章を読むたびに、子どものころに小さな実験で現実の秘密を解き明かし始めたときの、あのわくわくした興奮がよみがえる。本当に愛らしい
幸い最近は、このタイプに当てはまる人をより多く見つけるようになった
それでも Julia の文章はどれも、先ほど述べたあの高揚感を抱かせてくれる
「新人が『これは難しい』と言うと、経験豊富な人が『そうだよ、bash は使えない。誰もちゃんとは知らない』と言う」という話は、文字どおりに受け取る内容ではないと思う
その意味は、「自分たちが書いた bash コードへの理解や、テストしていない状況でも期待どおり動くという確信が強くない」に近い
少しでも普通でないことが起きれば何かが失敗し、bash について新しく知った事実に身震いしたり、近くの物を傷つくほど強く叩いたりすることになるだろう、とある程度予想しているという意味だ
Bash は複雑な言語であり、ほとんどのプログラマーにとって普段使う他の言語とはまったく違う。たいていの会社にはどこかの本番環境に多少の bash があるが、それを十分多く使っていて詳しい人が一人もいないことが多い
ビルドツール、CI ツール、クラウドオーケストレーションツールが、シェルスクリプティングの必要性を減らす方向へ進化しているのは偶然ではないと思う
思考実験として、bash にもっと良い代入文を追加できないだろうか。たとえば
set --goodassのようなモードでa = string1 + '.' + string2のように書けるなら、シェルのクォート処理のかなりの部分を削れるmakeのようなツールも恩恵を受けるはずだ。makeに 6 か月を費やして、使える変数、パスやファイル名を操作する明確な方法、より使いやすいターゲットを作るなら、複雑な Makefile を 6 か月かけて作るより良いかもしれない特に「ほとんどのプログラマーにとって bash は普段使うどんな言語とも違う」という感覚は、初心者が必ず推論できるものではない。「一般的でない」と「極度に難解」の違いが分かるだけの経験が必要だからだ
関連して、ほとんどのソフトウェアは過剰設計されている
業界の中央集権化のせいでもあると思う。少数のツールを支配する少数の人々に利益が出るよう、皆をそのツール群へ押し込み、その結果、多くのツールが「あらゆるもののためのツール」になって、対処すべきユースケースをはるかに超えて覆うようになる
企業は開発者全員に同じツールを知っていてほしい。そうすればプロジェクト間や会社間で簡単に置き換え可能になり、業界内での交渉力が弱まるからだ
そのためソフトウェアには一本の主流だけが残り、代替的なアプローチは仕事もなく排除される。業界は本来自然に分散したがっているのに、そうできていない
前向きに見れば、いつかはるかに優れた非主流のアプローチが現れ、主流のアプローチを侵食するだろう。技術は数学でもなく科学とも違い、同じ問題を複数の方法で解く多くの分岐を十分に支えられる
当時はブラウザ戦争が私たちを忙しくさせていたが、今はブラウザ同士がおおむね互換になっているにもかかわらず、たいてい不要な Web アプリ用フロントエンドの複雑さを大量に生み出している
DNS、IP、HTTPS のようなものは、後方互換性と政治的要素が絡んだ根本技術なので仕方ない
それでも、それらをしっかり学ぶほうが、フレームワークを学ぶより良い投資だと感じる。これ以上言うと、イノベーショントークンの話にまでなりそうだ
難しいものを簡単にするには、適切な抽象化を見つける必要がある。難しい内容の一部と、よく使う細部だけを頭に入れておき、残りは必要になったときに調べる、という形だ。
問題は、人は本当に必要になるまで、大きなテーマについての認知的な圧縮をわざわざ作ろうとしないことにある。すでに別の大きな認知負荷を抱えているので、新しい負荷を追加することに抵抗する。
あるテーマ X に詳しい別の人に頼れるなら、単にそうしてしまい、X を十分に知るために努力しないこともできる。X に詳しい人が助けを求められる回数を減らす最善の方法は、他の人が X の最小限を理解できるように手助けすることだ。
set -eは壊れているし、あらゆるものを引用符で囲まなければならない必要性も壊れている。グロビングは明示的に要求する機能であるべきだ。コマンドラインでは面倒だろうが、スクリプトでは話が違う。現状ではグローバルにグロビングをオフにすると、使いたい場所でグロビングするのが難しくなる。こうした悪いデフォルトは Bash だけでなく、Ksh や Bourne shell 系譜のシェル全般にある。
SQL についても、句の順序を変えたいと思っている人は多い。できない理由はなく、既存の SQL パーサーが別の順序の句を許容するようにする、比較的小さな変更で済みそうだ。
ただ個人的にはこの認知上の問題はない。おそらく、まずテーブルソースを見るものだと分かっているからだと思う。
set -eが壊れていること、すべてを引用符で囲まなければならないこと、グロビングは明示的であるべきこと、という 3 つの落とし穴は、OSH が既存のシェルスクリプトを実行しつつ修正してくれる。スクリプトの先頭に
shopt --set ysh:upgradeを追加すれば、その 3 つの問題はなくなる。プロジェクトを手伝いたいなら、tarball をダウンロードして主張を検証し、ブログ記事を書いてくれるとありがたい。
詳細は https://www.oilshell.org/release/latest/doc/error-handling.h... と https://www.oilshell.org/release/latest/doc/simple-word-eval... にある。
ドキュメントは網羅的だが、たいていの人はそこまで細かい情報を求めていないので、誰かがテストして短く書いてくれると助けになる。
しばらく Oils を積極的に推していなかった理由は Python 依存があったからだが、今では純粋な C++ になっており、今週時点で一部の計算中心のベンチマークでは bash に勝っている。
入出力中心のスクリプトは、ほとんどのシェルスクリプトがそうであるように、常に同じ速度だった。ドキュメントでは Oil をまだ YSH に変える必要があるため、しばらく混乱があるかもしれない: https://www.oilshell.org/blog/2023/03/rename.html
set -eのような秘伝の知識を覚えるのに時間を無駄にすべきではない。それでも今は検索エンジンがある。selectのように動作するが、正しい位置に置けるprojectを導入することかもしれない。私の超能力はひどい記憶力だ。だから覚えるには必ず理解しなければならない、つまり認知的な圧縮が必要になる。普通の人のようにただ学ぶことができない。
今日学んだこと:
&&や||のリストで実行されたコマンドのうち、最後の&&または||の後にあるコマンドを除いて、失敗したコマンドがあってもシェルは終了しない参考: https://www.gnu.org/software/bash/manual/bash.html#index-set
/bin/falseがしていることは 1 を返すことだけである。それは失敗なのか? そうではない。そのように動作するよう設計されており、文字通りその目的を持つツールである何百ものシェルスクリプトを書いてきたが、その中の多くのコマンドは、文字列が特定のパターンを持つか確認するなど、自分の仕事をするためにごく正常に 0 以外の値を返す
プログラムはどんな状況でも望む終了コードを返すことができ、慣例上、成功は 0、失敗は 0 以外の値である。しかしシェル言語が気にするのは、0 が「真」、0 以外の値が「偽」と評価されるという点だけである
何らかのプログラムが 0 以外の値を返すたびにシェルが終了するなら、
if文やループは不可能になり、非常に不便だろう特定のプログラムの戻りコードを重要視するスクリプトなら、明示的に確認して処理すべきである。リンク先にあるように、内部コマンドが 0 以外の値を返すとシェルを終了させるオプションがあり、多くの初中級のシェルスクリプト作者は、すべてのスクリプトでそれらを使うべきだと教条的に主張する
しかし複雑なスクリプトでは、かなりハック的で扱いにくいエッジケースが多いと感じる。毎回そのようなオプションが必要なら、むしろ Makefile を使うほうがよいのかもしれない
&&と||は条件文のようによく使われるからである[ -e README ] && cat READMEは README ファイルがないときにエラーを避け、[ -e README ] || echo "You should write a README!"は逆に動作するさらに厄介なのは、
set -eを前提にしても、パイプラインでは最後のコマンドが失敗しない限りシェルが終了しないということだgrep foo README | sortは、set -o pipefailも使わない限り、README がなくても失敗しない関数内で明示的に
set -eを設定しても、それすら上書きされる以前に例を挙げたことがある: https://news.ycombinator.com/item?id=22213830
難しくないはずに見えるが、実際には多くの複雑さがあるものをうまく描写した記事である
ただし SQL の部分は、謎を解くというより、概念的な失敗をさらに押し進めているように見える
クエリの論理は宣言的であり、出力を定義する。実行順序や手続き的な性格を持つのは クエリプラン である。これを先に学ぶべきだ
その後で、相関サブクエリのような曖昧な領域を学べる。
not existsとアンチジョインが等価であることが見えれば、理解し推論できる書かれたクエリを手続き的に理解せよという比喩は、問題を先送りするだけであり、より複雑なものに行き詰まったときに、善意の嘘を解きほぐす方法がなくなる
素晴らしい発表だった。Bash が「罠」と雑学でいっぱいで、すべてを覚えるのは難しいというのはその通りだが、一部の雑学は覚えておくのもよいと思う
たとえば
findコマンドの引数の順序をよく忘れて、すぐにインターネット接続できないマシンの前で構文を思い出すのに時間を失うことがあったそこで、最も一般的なコマンドラインツールとその 罠 の一部を学んで覚えることにし、Anki といくつかの記憶術を使った。投資対効果は十分に価値があったと思う
実際、jvns.ca の書籍推薦を見て Michael W. Lucas の Networking for System Administrators を読み、技術知識と少なからぬシステム管理者としての知恵を Anki カードに抽出した
今ではトランスポート層の問題をデバッグするときに、netcat や tcpdump のようなツールをすぐにどう使うべきか思い出せるので、読んだ本の中でも投資対効果が最も高い一冊かもしれない
最後に実行したコマンドをこのファイルに追加するショートカットと、このファイル内を検索するショートカットも作ってある
bash の man ページは巨大で複雑だが包括的である。主要なセクションとテキストの視覚的な形に慣れていれば、素早くめくって必要な正確な情報を見つけられるので、かなり役に立った
この方法がインターネット検索エンジンを使うより速いことも多い
古い man ページをテキストエディタに優しい形式へ変換したり、tldr や Dash のようなより良いツールを使ったりする方法である。
findだけがそうなのではないからだ理由は分からないけれど、この記事を嫌いになりたい気分だった。jvnsをHNで見かけすぎていたのか、単に機嫌が悪かったのだと思う
でも本当に良い記事で、20年の開発経験を持つ者として、プログラミングに関するメタレベルの議論の中ではかなり真実に近いと思う
選択的な視野の話は、
digにもmanページにも本当に当てはまる。manを開いて、果てしない設定オプションやコマンドラインフラグに圧倒されたことは数え切れないmanで使っているコツは、Vim風の検索機能である/を使うこと。たとえば grep で各マッチの行番号を出力する方法を探したいのに思い出せないなら、man grepを開いて/lineと入力し、Enterを押してmanページ内の「line」の出現箇所を検索する。次のマッチは単に/でよいStrange Loopが終わったという知らせも少し悲しい。昨年あたりにようやく知ったのだが、多くの発表は例外的に質が高いように見えた
終わってしまったのは悲しいが、時には何かが終わることにも良い面があるのだと、かなり説得力をもって示している。発表全体を見れば分かる
それに https://github.com/kristopolous/mansnip も作った
nを押してもよいbashに対する見方には強く同意しない。最善の解決策はbashの上にツールを載せたり、癖を覚えたりすることではなく、bashを使わないことだ
それが落とし穴を避ける唯一の方法だ
最も一般的な代替案は、1) Oil shell [0] のような新しいシェルを使う、または 2) Python、JavaScript、PHPのようなプログラミング言語を使うこと
新しいシェルの問題は、スクリプトを使いたい場所ごとにそのシェルをインストールしなければならない点だ。一方でbashはどこにでもある。自分だけが保守するスクリプトでないなら、他の人にもそのシェルを覚えて保守してくれと求めることになる
他のプログラミング言語の問題は、bashが得意なこと、つまりコマンドをつなぎ合わせ、コマンドの入出力とファイルを扱う作業の使い勝手がまれに見るほど良い点だ
他の言語でやろうとすると、急にずっと複雑になるか、少なくとも冗長になる
だから今もbashを使っているが、その強みは他のコマンドを実行し、入出力を扱うところにあると認めている。それらに関係しない複雑なロジックなら別の言語に渡す。場合によってはbashを完全に避けるのではなく、bashからPythonスクリプトを呼び出す程度だ
別のやり方のほうがうまく合ったなら共有してほしい
[0] https://www.oilshell.org
その新しいツールには、bash自体が経てきた何十年ものデバッグもないはずだ。問題はbashそのものにある
私たちは使いやすさを過小評価し、「賢さ」を過大評価しがちだ
代表例がGitだ。非常に賢いツールだが、使いやすさはひどい。それでもLinusが作ったもので、Linusは賢いので、問題はこちらにあるかのように思われる
私たちは自分たちが価値を置くものを手にする。使いやすさにもっと価値を置くべきだ
最善の解決策は単に距離を置くことだ。本当にやめるべきだ。マッチョぶろうとしてはいけない
言語全体のモデルが根本的に壊れている。文字列中心の型、グローバルなモードスイッチ、基本的な比較演算子に一文字フラグ、あちこちでエラーをデフォルトで無視する挙動、特に関数まである
こうした癖が一つあるだけでも言語を候補から外すには十分なのに、bashにはそれらがすべてあり、それ以上もある
set -xが||と&&の期待される動作を壊し得る点のようにそもそも関数がfalseを返すと衝突する言語があるだろうか。例外を投げる言語はあるが、「false」は返してよい有効な値ではないのか
Makefileでも同じことが見られる。人々は自分が何をしているのか理解しておらず、ビルドシステムについて深く考えたことがないため、特定の方式で動くことを期待する
たとえばMakeの再帰代入は、ほとんど全員をつまずかせる
FLAGS=-b、COMPILE=compile $(FLAGS)、$(info compile command=$(COMPILE))、FLAGS=-a、myfile:、echo $(COMPILE) $? -o $@は、最初の情報出力ではcompile -bが出るが、実際の実行はcompile -a -o myfileになるだからといって他のプログラミング言語に合わせるためにすべての代入を即時評価にしてしまうと、非常に有用な道具を奪うことになる。こうした道具を理解すればするほど、どこで使うべきか、どれだけ労力をかけるべきかがよく分かる
それでもおおむね同意する。少しでも複雑なものは、より癖の少ない言語で書いたスクリプトに渡すようにしている
そのときbashとして残る最後の5%でミスを避けるのを助けてくれるツールは、とても有用だ