すべてのコマンドをカンマで始めよう (2009)
(rhodesmill.org)- Unix ユーザーが
~/bin/に個人用スクリプトを置いてPATHに追加すると、短いコマンド名が新しい システムコマンド と衝突する可能性がある - Debian/Ubuntu のように提供コマンドが多い環境では危険性がさらに高くなり、例として Ubuntu ノートPCの
/usr/binにあるコマンドは 21,733個 と数えられる - 個人用コマンドの先頭に カンマ(
,) を付けると、シェルやツールはそれを通常のファイル名文字として扱いながら、システムコマンドとも簡単に区別できる - カンマは Shift なしで入力でき、シェルで意味の強い括弧・バックスラッシュ・コロン・バッククォート・シングルクォート・スラッシュ・ドットより衝突の余地が少ない
,の後に Tab を押せば個人用コマンドの一覧をすぐに見渡せるため、~/bin/のコマンド名をすっきり保ちやすい
個人用コマンド名が衝突する理由
- 多くの Unix ユーザーはホームディレクトリに
~/bin/を作り、PATHに追加して個人用の便利コマンドやシェルスクリプトを使っている - 問題は、個人用スクリプトの名前がたいてい短い 小文字の組み合わせ であり、標準のシステムコマンドと似やすい点にある
- Linux ディストリビューションが新しいコマンドを追加すると、既存の個人用コマンドと名前が偶然同じになることがある
- Debian 系の環境では提供コマンド数が多く、この問題がより現実的になる
- 例として Ubuntu ノートPCで
/usr/bin直下のコマンド数を数えると21,733個になる apt-file search -x '^/usr/bin/[^/]*$' | wc -l
- 例として Ubuntu ノートPCで
カンマ接頭辞がもたらす利点
- 解決策は、個人用コマンド名を入力しやすく、しかもシステムコマンド名としては選ばれにくい形に変えることだ
- 入力しやすさの基準は Shift キーを使わないこと であり、この条件を満たしながら安全な文字は多くない
- 小文字はすでにシステムコマンドでよく使われている
- 括弧、バックスラッシュ、コロン、バッククォート、シングルクォートはシェルで特別な意味を持つ
- スラッシュはディレクトリ区切り文字なのでファイル名に入れられない
- ドットはファイル名の先頭では隠しファイルを意味し、ほかの位置でも拡張子の区切りによく使われる
- 残る選択肢は単純な カンマ(
,) であり、周辺ツールやシェルはカンマをファイル名中の通常の文字として扱う - 個人用コマンドごとにカンマを接頭辞として付ければ、システムコマンドと明確に区別でき、名前の衝突を避けやすくなる
- Tab 補完と一緒に使えば、
,を入力したあとに個人用コマンド一覧をすぐ確認できる- 例の一覧には
,complete-scp,,complete-ssh,,coreoff,,coreon,,find,,go-thpgp,,gr,,hss,,mount-thpgp,,mount-twt,,range,,svn-store-password,,umountなどが含まれる
- 例の一覧には
- この方法は約10年間使われており、個人の
~/bin/コマンド名をきれいに整理する方法として勧められている
2件のコメント
Hacker Newsの意見
タイトルだけ見たときはひどいアイデアだと思ったが、実際にはかなり気に入った。特にタブで自分のツールを全部一覧できるところがよかった。
最近は名前空間の衝突をそれほど経験していないし、管理職に移ってから技術感覚が少し鈍ってきたのも事実だ。自分の技術スタックが10年くらい古びている感じなのだが、また最新の感覚に追いつくにはどこから始めるのがよいのか気になる。
私のやり方は、自分の生活を楽にするツールを自分で作ることだ。たとえば会社で単純な参照によく使うWebサービスがあればAPIがあるか調べ、日常作業を速くするためにCLIを書く。気に入るように磨き込んでからチームに共有するが、他の人に説得して使ってもらうのは難しい。それでも毎日自分が使っているので、あまり気にはしていない。
新しい視点を得てトレンドについて話せるようにするのが目的で、その一部は今では仕事にも入ってきている。おかげで古い構成要素についても、より深く理解できるようになった。
この問題がよく理解できない。自分の
binディレクトリを$PATHの末尾ではなく先頭に置けばいい。自分のコマンドを見て回るには、単にls ~/binを実行する。$0がシステムパスにあることを期待しているのに、それが壊れて面倒なデバッグが始まる可能性がある。結局は、どちらの毒を選ぶかという問題だ。
fzf補完を使えることだ。たとえばfishでは、コマンドの最初の文字を打ってTabを押すとfzfを起動できる。すると
,+Tab だけでユーザー定義コマンドを素早く絞り込める。一方でls ~/binは頻繁にやる作業にしては入力する文字が多いし、あるいはls+上矢印を何度も押して、まず補完候補を探さなければならないかもしれない。ls ~/binは,より入力がずっと遅い。私は
gitを薄く包んだラッパーに、aa、st、di、dp、cm、leのような短いユーザー定義コマンド名を使っている。このうち一つは、あるシステムにデフォルトでインストールされるユーティリティと実際に衝突する。それでも自分の
binディレクトリがシステムディレクトリより$PATHの前にあるので自分のものが勝つし、衝突しているそのツールにはあまり関心がない。自分に有用な別のツールが自分のツールと衝突するなら、自分のツール名を変えるより、その別のツールに衝突しない別名を付ける可能性が高い。この2文字ツールたちが便利すぎる。apt-get upgradeが Dwarf Fortress を起動するような問題につながり得る。https://askubuntu.com/questions/938606/dwarf-fortress-starti...
gitエイリアスはたいていgで始まる2文字の組み合わせにしている。たとえばgsはgit statusだ。しかし、ときどき本当に GhostScript が必要なことがある。PDFファイルにフォントを埋め込むときなどには非常に優秀だ。普通そういうときは
env gsを使う。jで始まる。javaが登場したときはなかなか面白いことになった。カンマを使うのはかなり興味深いアイデアだ。それでも
kで始めなかったのは幸いだった、KDEがあるからね :)たとえば
mkfsはsys::mkfsのように呼び出すべきだと思う。システムコマンドとユーザーコマンドを分ける線はいろいろな方法で定義できるし、曖昧な部分もあるだろうが、ユーザーが存在すら知らず、明示的にインストールした覚えもないコマンドを誤って実行できるなら、それはグローバル名前空間に直接さらされるべきコマンドではない。関連する質問がある
私はほとんど Windows を使っていて、筆者のように Python 中心の CLI スクリプトをいくつも作り、
~/bin/に相当する場所に置いている。python.exeを.py拡張子の既定のプログラムに設定し、.pyを%pathext%に追加すると、どのパスからでもhelloと入力するだけで~/bin/hello.pyを実行でき、1日に何百回も使っている。最近は Linux を使うことが増えてきたが、まだ初心者なので同じようには作れていない。Linux には「関連付けられたプログラム」という概念がないようで、.pyファイルをそのまま呼び出して、シェルに Python で実行させることができない。もちろんスクリプトにchmod +xを付けることはできるが、そうするとスクリプト自体にシバンを入れなければならず、ハードコードのように感じられて気持ち悪い。後で.pyスクリプトを/usr/bin/pythonではなく/usr/bin/nohtypで実行したくなったらどうするのか、と思ってしまう。さらに、スクリプトを呼び出すときに.pyの部分を省略する方法も見つけられていない。Linux の設計を批判したいわけではなく、利点が多いことは分かっているが、本当に$PATHにあるhello.pyをhelloとして実行したいmy_pythonというシンボリックリンクを置き、シバンを/usr/bin/env my_pythonにすればよいもっと原則的なアプローチが欲しいなら、
update-alternativesツールを見るとよい。この種の抽象化をより一般的に提供している: https://linuxconfig.org/how-to-set-default-programs-using-up...Linux では、実のところ Windows 以外のほとんどのプラットフォームでは、ファイル拡張子の意味ははるかに弱い。どの種類の実行ファイルであれ、実行可能かどうかは拡張子ではなく
+xフラグなどで決まる。そのおかげで、実装言語を変えても呼び出し側を壊さずに書き直せる。.py拡張子はインポートして使うモジュールには意味があるが、実行されるスクリプトなら、必要なときにシバンを見ればよい。外部配布のスクリプトは通常#!/usr/bin/env pythonを使い、ディストリビューションのパッケージに含まれるスクリプトは#!/usr/bin/pythonなどに上書きされる。また、シバンは複数の引数をサポートしないが、GNUenvはそれをまねられる-S引数をサポートしている。ただし引数の長さの問題は残る.pyを外せばよい。"hello"と呼ぶのはまったく問題ないシバンの欠点はあまり思い浮かばない。本当に別のインタプリタで実行したいなら、
"nohtyp hello"のように明示すればよい。それでも気になるなら、シェルの起動ファイルでエイリアスを定義できる。たとえば bash ではalias hello="python3 /path/to/hello.py"のようにできる。やる気があれば、指定したディレクトリの内容についてこうしたエイリアスを自動生成する短いスクリプトも書けるシェルスクリプトでは通常、シバンを追加してファイルを実行可能にし、実行ファイルをスクリプト自身の中で宣言する。シバンを一種のファイル拡張子のように考えればよい。
chmod +x ./malware.pyの後で./malware.pyが動作しなければ、シバンが指しているパスを確認すればよい。インタプリタがスクリプトを通常の引数として実行できるなら、xdg-open malware.pyのような方法でも似た動作を作ることはできる。既定のファイルマネージャでダブルクリックするのと同じはずだ。Linux を主なデスクトップ OS として使っていたとき、xopというエイリアスを置いていたことがあるが、すでに既定の動作が合っている画像や文書のようなデータファイルにだけ使っていた。実行可能なスクリプトの既定プログラムをインタプリタに設定するのは勧めない。既定ではスクリプトを実行せず、エディタで開きたいこともあるだろう。xdg-openは Gnome 系のツールだと思うが、他のデスクトップで使えないという意味ではなく、Xubuntu でも使っていた。本当にすべての Python ファイルを GUI の文脈でも既定で実行したいなら、そういう既定値を設定することはできるが、man xdg-openが役に立つかもしれない。繰り返すが、よい助言ではない#!/usr/bin/env pythonだこうすると、パス上で最初に見つかった
pythonが実行される。後で/usr/bin/pythonではなく/usr/bin/nohtypで実行したくなったら、/usr/binより先に検索されるディレクトリに、/usr/bin/nohtypを指すpythonシンボリックリンクを作ればよい。たとえば~/myCommandPreferencesを$PATHの先頭に追加すればよい$PATHの衝突を避ける別の方法は、他の実行ファイルが使う可能性の低い、とても長い実行ファイル名を作り、bashrcに短いエイリアスを置くことだエイリアスはスクリプト内部で呼び出される実行ファイルには影響せず、自分のスクリプト内では長い名前で参照し続けられる。欠点は、同じレベルの タブ補完の使いやすさ が得られない点だが、その部分は本当に素晴らしい。ただし、Python の
venv有効化スクリプトのように、サブプロセスとして実行されるのではなくsourceされなければならないスクリプトでは、依然として衝突が起きる可能性がある。ただ、そういうケースはまれだzshではその補完もできるカンマで始める方式は、テキスト展開/テキスト置換コミュニティでもよくある手法だ
,で始まる最近
~/.local/bin/を見ていて、自分で入れた覚えのない実行ファイルが何十個もあることに気づいたほとんどは pyside 関連だったが、他のスクリプトもあった。どのスクリプトを自分で書き、どれが他人の作ったものなのか思い出すために、一つずつ開かなければならなかった。自分のスクリプト名をカンマで始めていたらずっと早かっただろうし、各スクリプトをなぜ作ったのかも、開く前に思い出す助けになったはずだ
~/.local/bin/はインストールされたスクリプト用で、ローカルで自分で書いたものは~/bin/に置く遠慮しておく。個人用の
binを$PATHの先頭に置き、隠れてしまったプログラムを参照するときは/usr/binや/binを使えばよい個人化したツールの一覧は
~/bin/[Tab]で表示できるシステムの
grep、たとえば Solaris のgrepが気に入らず、自分の好む GNUgrepを使いたいなら、なぜそれを単にgrepとして置いておかないのか?このアイデアに5年前に触れたおかげで、自分のシェル小技集に秩序を持たせることができた。エイリアスと
~/binを合わせてカンマコマンドが50個以上あり、以前のごちゃ混ぜの状態よりシェル生活がずっとスムーズになった2020年にも議論されていた: https://news.ycombinator.com/item?id=22778988(コメント90件)
, macroexpandの結果があるStart all of your commands with a comma (2009) - https://news.ycombinator.com/item?id=31846902 - 2022年6月(コメント121件)
Start all of your commands with a comma (2009) - https://news.ycombinator.com/item?id=22778988 - 2020年4月(コメント89件)
'_'を使うのはどうでしょうか?