シェル:利用可能なプログラムの選択

シェル:利用可能なプログラムの選択

$PATHbash / ksh / zshで見つけることができる(またはシェルから呼び出すことができる)代替リストの最初の変数を設定する良いイディオムはありますか?

たとえば、動作する必要があるスクリプトがある場合実際のパス(1)下に設置しrealpathたり、grealpath長い道のりを行くことができます:

 if type "grealpath" > /dev/null; then
    realpath_exec=grealpath
  elif type "realpath" > /dev/null; then
    realpath_exec=realpath
  else
    echo "$0: No realpath found in PATH" >&2
    exit 1
  fi

式を書くために私が考えることができる||ほとんどの方法は、入れ子に&&なったサブシェル(大きな問題ではなく、頻繁に呼び出される関数のパフォーマンスに悪くも面倒です)および/またはさまざまな出力を処理するための複雑なgrepリダイレクトまたは醜さを必要としますします。 (参考にしてください、出力インストールされている「プログラム」が関数またはエイリアスの場合は、type直接使用できます。whichしたがって、操作を実行するために何かを呼び出すことができると仮定するには、realpath出力または別名の代わりに戻り値をオンにする必要があります。 grepをやってみてください。 )

上記の内容は素晴らしいです。そのようなプログラムに複数のオプションがある場合は、時間がかかり、苦痛です。もっとありますか?エレガントリストから最初に利用可能なプログラムを選択して変数に割り当てることはできますか?

ベストアンサー1

最も簡単でわかりやすいのは、for内部にifsがあるループであり、break一致するものを見つけるとループから抜け出します。


これをしたくない場合は、Bashでtype複数のパラメータを受け入れます。探す:

type grealpath realpath ...

一致する項目の出力行の最初の単語は、常に1コマンド/関数/エイリアスの名前です。追加のサブプロセスを避けるために、配列のstdoutをキャプチャしてインデックス化できます。

words=($(type realpath grealpath foo make 2>/dev/null))
realpath_exec=${words[0]}
if ! [ "$realpath_exec" ] ...

これはいくつかの観点からはエレガントですが、偶然発見した場合は明らかに理解するのがより困難です。


配列イニシャライザのトークン化に依存するの価値IFSIFSこれが変更された場合、またはコマンドにスペースが含まれている場合は機能しません。


1実験によると、そうです。最大ロケールは自然な語順ではなくても、常に U+0020 ASCII スペースが続きますが、出力形式は指定されなくなりました。一部のローカライズではこれは機能しません。これが問題を引き起こすかどうかを考慮する必要があります。LC_ALL=C type ...(多少)適切であると保証された出力フォーマットを使用できます。


zshから、関数やエイリアスではなく実行可能ファイルにのみ興味がある場合は、次のようにします。$commands:

realpath_exec=${commands[grealpath]:-${commands[realpath]:-${commands[another]:?No compatible command found}}}

拡張はnullでない場合は値を提供し${param:-...}、それ以外の場合はエラーが発生します。これはまだ非常に醜いです。param...${param:?...}...param

コマンド選択の間の順序に気にしない場合は、より単純なバージョンを使用できます(i) 下付き文字の表示:

realpath_exec=${commands[(i)realpath|grealpath|another]}

関数とエイリアスを含めるには、$functionsandを使用できますが、$aliases繰り返します。

おすすめ記事