2 ポイント 投稿者 GN⁺ 2024-06-24 | 2件のコメント | WhatsAppで共有
  • 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

カンマ接頭辞がもたらす利点

  • 解決策は、個人用コマンド名を入力しやすく、しかもシステムコマンド名としては選ばれにくい形に変えることだ
  • 入力しやすさの基準は 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件のコメント

 
GN⁺ 2024-06-24
Hacker Newsの意見
  • タイトルだけ見たときはひどいアイデアだと思ったが、実際にはかなり気に入った。特にタブで自分のツールを全部一覧できるところがよかった。
    最近は名前空間の衝突をそれほど経験していないし、管理職に移ってから技術感覚が少し鈍ってきたのも事実だ。自分の技術スタックが10年くらい古びている感じなのだが、また最新の感覚に追いつくにはどこから始めるのがよいのか気になる。

    • 最近のクールなやり方は、余暇に自分で何かを作り、その過程で自然に学ぶことのように思う。自給自足、創造性、自分から始める姿勢が今の感覚で、たくさん作るほど実際の問題を解くコツや現代的な解法を自分で見つける動機が生まれる。
    • 私も管理側にいて、追い続けないと技術が退化しそうだと感じている。
      私のやり方は、自分の生活を楽にするツールを自分で作ることだ。たとえば会社で単純な参照によく使うWebサービスがあればAPIがあるか調べ、日常作業を速くするためにCLIを書く。気に入るように磨き込んでからチームに共有するが、他の人に説得して使ってもらうのは難しい。それでも毎日自分が使っているので、あまり気にはしていない。
    • 同じ理由で、仕事で慣れているスタックではなく、より流行している別のスタックでサイドプロジェクトを作った。
      新しい視点を得てトレンドについて話せるようにするのが目的で、その一部は今では仕事にも入ってきている。おかげで古い構成要素についても、より深く理解できるようになった。
  • この問題がよく理解できない。自分の bin ディレクトリを $PATH の末尾ではなく先頭に置けばいい。自分のコマンドを見て回るには、単に ls ~/bin を実行する。

    • そうすると、あるツールが $0 がシステムパスにあることを期待しているのに、それが壊れて面倒なデバッグが始まる可能性がある。
      結局は、どちらの毒を選ぶかという問題だ。
    • 単に自分で付けた名前を覚えておけばいいのでは? これがなぜ一種のハックのように扱われるのか分からない。
    • 利点の一つは fzf 補完を使えることだ。たとえばfishでは、コマンドの最初の文字を打ってTabを押すと fzf を起動できる。
      すると ,+Tab だけでユーザー定義コマンドを素早く絞り込める。一方で ls ~/bin は頻繁にやる作業にしては入力する文字が多いし、あるいは ls+上矢印を何度も押して、まず補完候補を探さなければならないかもしれない。
    • ls ~/bin, より入力がずっと遅い。
  • 私は git を薄く包んだラッパーに、aastdidpcmle のような短いユーザー定義コマンド名を使っている。
    このうち一つは、あるシステムにデフォルトでインストールされるユーティリティと実際に衝突する。それでも自分の bin ディレクトリがシステムディレクトリより $PATH の前にあるので自分のものが勝つし、衝突しているそのツールにはあまり関心がない。自分に有用な別のツールが自分のツールと衝突するなら、自分のツール名を変えるより、その別のツールに衝突しない別名を付ける可能性が高い。この2文字ツールたちが便利すぎる。

    • こういうやり方は、apt-get upgradeDwarf Fortress を起動するような問題につながり得る。
      https://askubuntu.com/questions/938606/dwarf-fortress-starti...
    • その通り。1〜3文字の名前は、ユーザーのエイリアス、関数、スクリプトと標準ユーティリティのために残しておくべきだ。
    • 私の git エイリアスはたいてい g で始まる2文字の組み合わせにしている。たとえば gsgit status だ。
      しかし、ときどき本当に GhostScript が必要なことがある。PDFファイルにフォントを埋め込むときなどには非常に優秀だ。普通そういうときは env gs を使う。
    • 私の個人コマンドはすべて j で始まる。java が登場したときはなかなか面白いことになった。カンマを使うのはかなり興味深いアイデアだ。
      それでも k で始めなかったのは幸いだった、KDEがあるからね :)
    • やや強い意見だが、システムコマンドはユーザーコマンドと同じくらい簡単にアクセスできるべきではないと思う。何らかの形で名前空間があるべきだ。
      たとえば mkfssys::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.pyhello として実行したい

    • Linux では、やはり シバン がこの問題に合った道具だと思う。手軽にやるなら、パス内に 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 などに上書きされる。また、シバンは複数の引数をサポートしないが、GNU env はそれをまねられる -S 引数をサポートしている。ただし引数の長さの問題は残る
    • ファイル名から単に .py を外せばよい。"hello" と呼ぶのはまったく問題ない
      シバンの欠点はあまり思い浮かばない。本当に別のインタプリタで実行したいなら、"nohtyp hello" のように明示すればよい。それでも気になるなら、シェルの起動ファイルでエイリアスを定義できる。たとえば bash では alias hello="python3 /path/to/hello.py" のようにできる。やる気があれば、指定したディレクトリの内容についてこうしたエイリアスを自動生成する短いスクリプトも書ける
    • そういう概念はあるにはあるが、シェル構文の中にはない。普通はデスクトップ/GUI に委ねられる アプリケーションレベルの問題
      シェルスクリプトでは通常、シバンを追加してファイルを実行可能にし、実行ファイルをスクリプト自身の中で宣言する。シバンを一種のファイル拡張子のように考えればよい。chmod +x ./malware.py の後で ./malware.py が動作しなければ、シバンが指しているパスを確認すればよい。インタプリタがスクリプトを通常の引数として実行できるなら、xdg-open malware.py のような方法でも似た動作を作ることはできる。既定のファイルマネージャでダブルクリックするのと同じはずだ。Linux を主なデスクトップ OS として使っていたとき、xop というエイリアスを置いていたことがあるが、すでに既定の動作が合っている画像や文書のようなデータファイルにだけ使っていた。実行可能なスクリプトの既定プログラムをインタプリタに設定するのは勧めない。既定ではスクリプトを実行せず、エディタで開きたいこともあるだろう。xdg-open は Gnome 系のツールだと思うが、他のデスクトップで使えないという意味ではなく、Xubuntu でも使っていた。本当にすべての Python ファイルを GUI の文脈でも既定で実行したいなら、そういう既定値を設定することはできるが、man xdg-open が役に立つかもしれない。繰り返すが、よい助言ではない
    • 目的を直接達成してくれるわけではないが、シバンは半分だけハードコードだ。シバンを使う「正しい」方法は、いくつかの注意点は https://unix.stackexchange.com/a/29620 を見てもらうとして、#!/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 ではその補完もできる
  • カンマで始める方式は、テキスト展開/テキスト置換コミュニティでもよくある手法だ

    • その通り。私の vim エイリアスの大半も , で始まる
  • 最近 ~/.local/bin/ を見ていて、自分で入れた覚えのない実行ファイルが何十個もあることに気づいた
    ほとんどは pyside 関連だったが、他のスクリプトもあった。どのスクリプトを自分で書き、どれが他人の作ったものなのか思い出すために、一つずつ開かなければならなかった。自分のスクリプト名をカンマで始めていたらずっと早かっただろうし、各スクリプトをなぜ作ったのかも、開く前に思い出す助けになったはずだ

    • 通常 ~/.local/bin/ はインストールされたスクリプト用で、ローカルで自分で書いたものは ~/bin/ に置く
  • 遠慮しておく。個人用の bin$PATH の先頭に置き、隠れてしまったプログラムを参照するときは /usr/bin/bin を使えばよい
    個人化したツールの一覧は ~/bin/[Tab] で表示できる

    • システムユーティリティを自分のものに置き換えつつ、同じ名前を使いたくないというのはともかく、そのために常にカンマを覚えておかなければならない点が理解できない
      システムの grep、たとえば Solaris の grep が気に入らず、自分の好む GNU grep を使いたいなら、なぜそれを単に grep として置いておかないのか?
  • このアイデアに5年前に触れたおかげで、自分のシェル小技集に秩序を持たせることができた。エイリアスと ~/bin を合わせてカンマコマンドが50個以上あり、以前のごちゃ混ぜの状態よりシェル生活がずっとスムーズになった

  • 2020年にも議論されていた: https://news.ycombinator.com/item?id=22778988(コメント90件)

 
kayws426 2024-06-24

'_' を使うのはどうでしょうか?