現在のシェルインタープリター名を*確実に*かつ*簡単に*取得するにはどうすればよいですか?


9

(コマンドラインからではなく)スクリプトまたはソースファイル内から現在のシェルの名前を取得する簡単で信頼性の高い方法を探しています。私はちょうどそうすることを期待していました$(basename "$SHELL")が、ログインシェルがzshあり、some_script.shに次のコードがある場合

this_shell=$( basename "$SHELL" )
echo "The Shell is $this_shell"
echo "The Shell is $0"

そして私はそれを実行しbash some_script.sh、それはまだ示していますzsh代わりにbash使用インタプリタがあるにもかかわらず/bin/bash。シェルの設計者が現在のシェルではなくデフォルトのシェルを表示するように選択した理由はわかりませんが、それが私たちが行き詰まっているようです。

他にも少し似た質問がありますが(ここここ)、その答えは多くの点で不十分です。

  1. 多くの場合、ユーザーが現在使用している対話型シェルを把握しようとしていると想定しますが、それはおかしいです。シェルかを決定することができるようにどこでも実行することができ、私の必要性のコード-私はにコマンドを入力してるシェル知っている、それが使用されます。
  2. 彼らはしばしばいくつかの異なることを試みます-再びあなたがコマンドラインにいて、あなたがbashを使用していることを学ぶまでただいじくり回すことができるかのように-しかし、私はすべてのコンテキストで信頼できる単一のものが必要です。
  3. 彼らはしばしばのようなスクリプトから全く役に立たないものを与えecho $0ます。上記のスクリプトに示すように、これは失敗します。(はい、それはインタラクティブなシェルコマンドラインで機能しますが、なぜあなたが使用しているシェルを知らないのですか?)
  4. 彼らは時々(私の限られたテストでは)のような正しい情報を含むコマンドを提供しますがps -p $$、クロスプラットフォームでクロスシェル互換のsed / awkコマンドが不足しているため、パイプを介してシェル名のみを取得し、付随する他の情報を無視します乗る。
  5. 彼らは次のように、シェルのみのカップルで動作するものが含まれる$BASH_VERSION$ZSH_VERSION。私はそのような可能な限り多くのシェルとしてサポートしたいですfishcshtcsh

私はどのように行う、確実かつ正確に検出任意の 現在のシェルを?私は、プラットフォーム間で、スクリプト内で、そして可能な限り多くのシェルで機能する何かを探しています1

更新:私がこの質問を投稿したとき、私はこの情報を提供するためにシェルに組み込まれたいくつかの機能があると予想しましたが、そうではないようです。現在、シェルの外の何かに依存することは避けられないように思われるので、クロスプラットフォームソリューションを求めていることをより明確にする必要があります(上記の他の回答への私の反対には暗示されていますが、そうしないと簡単に見落とされる可能性があります)質問を注意深く読んではいけません)。

Update 2Stéphaneの回答がLinuxのみではないために、これがLinuxのみの質問の重複であるとまだだれもが信じている場合、私が求めているものと彼が提供したものとの違いは次のとおりです。(彼が書いたものは独創的であり、私はそれをノックしていませんが、それは私の問題を解決しないことに注意してください。)

  1. 私は何かを探していますシンプルかつ信頼性の高い、その
  2. (によって供給されることを任意のスクリプトや関数定義に追加することができ.zshrcたり.bash_profile、分岐または何でも)。
  3. 彼のスクリプトは、デフォルトのインタプリタによって常に解釈されて返されるため、インタプリタ名を呼び出し側のスクリプト/関数に渡す外部ユーティリティとして使用することはできません。これは私の目的に使用することを困難または不可能にします。可能であれば、それは非常に非常に困難であり、それを機能させるための解決策は答えに記載されていません。したがって、彼は私の質問に答えなかったため、それは重複ではありません。

あなたが何かを見たい場合になる仕事を、見とるGitHubの上ShellDetectiveを。これにより、SEに既に存在するものと、この質問が探しているものとの違いを簡単に確認できる場合があります(実際、この質問のニーズを満たすために作成されたもので、他では満たされていないものです)。


(PSあなたはにソース取得機能を想像し、このためのユースケースがあります信じることができない場合.zshrc.bash_profileまたは.profile使用されているものを、サーバーに依存し、どのようなシェルそれが利用可能です。彼らはシェバング行を持っていないので、彼らが調達している。彼らは、どのシェルでも動作する場合に最も便利ですが、動作方法を知るために、どのシェルにいるかを知る必要がある場合もあります。)


1伝統的な意味でのシェルではない「シェル」には関心がありません。誰かが自分で書いたシェルについては気にしていません。私だけに関係してる本当のシェル実際に野生で発生し、そしてその誰か多分彼らはちょうど彼らが好きなシェルいくつかのサーバにログインをインストールすることはできませんれているときに使用する必要がある場合があります。


Pythonがシェルであるかどうかについての完全にトピック外の議論を読むには、こちらを
iconoclast

2
Gilles質問に対する回答を引用する「ターミナル」、「シェル」、「tty」、「コンソール」の正確な違いは何ですか?  —「シェルは、ユーザーがログインしたときに表示される主要なインターフェースであり、その主な目的は他のプログラムを起動することです。」IMO、「シェル」のカテゴリからPerlとPythonを排除するには十分です。
G-Manは 'Reinstate Monica'を

漠然と関連:互換性スクリプト:$節約?後で使用するため。推奨されるアプローチ:と言ってさまざまなシェルの構文の違いを回避しsh -c "…"、POSIXシェル構文で作業の大部分を行います。
G-Manは 'Reinstate Monica'を

2
なぜあなたはそれを手に入れたいのですか?ログインシェルとして使用できるシェルには、非常に互換性のない構文があるものがあります(一部のシェルはLispのような言語です!)。ソース可能なファイルをコーディングする場合は、いくつかのバリアントをコーディングし、適切なものを選択するようユーザーに指示する必要があります。
Basile Starynkevitch、2015

1
2つ目の質問は、シェルを偽装する例です。名前がインタープリターが解釈する言語と一致しないインタープリター実行可能ファイルによってスクリプトが実行された場合はどうしますか?私がcshのコピーを取得した場合、名前をに変更し、それをfish使用してスクリプトを実行しますfishcsh
Gilles「SO-悪をやめる」

回答:


5

私はこの組み合わせで素晴らしい結果を出しました:

ps -p $$ | awk '$1 != "PID" {print $(NF)}'

Tru64(OSF / 1)では、出力のシェルが括弧で囲まれています。tr -d '()'それらを削除するために鋲で留めます。

ps -p $$ | awk '$1 != "PID" {print $(NF)}' | tr -d '()'  

Solaris 10およびRHEL 5.7 / 6.4のすべてのシェルで機能するようです。Debian、Ubuntu、ミントなどの他のディストリビューションはテストしませんでしたが、すべて同じように動作するはずです。また、FreeBSDが機能するかどうかもわかりません。

ここに画像の説明を入力してください


3
スクリプトでは機能しません(代わりにスクリプトの名前が返されます)
Qw3ry

2

だから私はまだこれを具体化しようとしていますが、私はうまくいく考えを持っていると思います。お気づきのように、実行しようとしているのは、不可能ではないにしても、すべてのシェルで実行するのが非常に難しいことです(ポリグロットに追加する各バリアントは、線形速度よりも複雑になります)。Bourne(sh、ash、dash、bash、ksh、pdksh、zshなど)とcスタイルのシェル(csh、tcsh、fishな​​ど)に分割すると、おそらくそれを行うことができますが、cshバリアント間のバリエーションは、さまざまな興味深いものを提示します挑戦。

そこで、既知の言語(shebang行、C、perl、pythonなどを使用したbash)で検出ルーチンをチートして記述し、親の実行可能ファイルの名前を特定しましょう。次に、2つのトリックを使用して、情報を親に戻します。つまり、戻り値と1つの単語を標準出力に書き込みます。shの下降シェルでは、すべてが良好なバックティック処理をしているため、0を返し、シェルの名前を書き込みます。シェルのCファミリでは、回避する必要がある非互換性のセットごとに、戻り値の増分を開始できます。200を超える戻り値は、すべてで始まるエラーを示しますが、私の親が200であるかどうかはわかりません。

内部プログラムはLinuxでは簡単に見えます(/proc/ppid/exe戦いの半分です)。これはbsdでpsオプションを使用して実行できると確信していますが、現時点ではテスト用の実行中のbsdシステムがなく、私のMacには新しいハードドライブが必要です(とにかく10.4しかありません)。シェル構文の問題は大幅に軽減されますが、互換性の問題の別のセットが導入されます。まだ可能性はあると思います。


これは私が最終的に解決した洞察、つまり作業を行うには外部プログラムが必要であるという洞察ですが、G-Manの別の質問sh -c "..."からのに関するG-Manのコメントを読んだときにそれがわかりました。(申し訳ありません。コードが見つからなかったため、最初は回答をスキップし、後で戻ってきてそれを読みました。)
iconoclast

@iconoclastの質問には逆説があります。彼は、任意のシェルからソースを取得できるスクリプト(一部のシェルは関数をサポートしないことに注意してください)からそれを呼び出したいので、そのスクリプトはおそらくポリグロットであるため、何が解釈されているのかを理解するためのサポートがすでに必要です。私の経験では、ポリグロットコードを記述します(私のwhich_intepreterは極端な例ですが、シェルのみのコードをここで参照することも非常に困難であり、一般に努力する価値はありません。
StéphaneChazelas 15

@StéphaneChazelas:努力に見合う価値がないことは確かだと思います(確かに一般的にはそうではなく、おそらくそうでないこともあります)でも、あきらめる準備はできていません...問題は本当に2つの問題に分類されます。シェル名と2番目の使用法 どちらも(少なくとも実際には)使用できるシェルの制限を回避するために、外部からの助けが必要です。Crystalで記述された小さなユーティリティを使用して最初の問題を解決しました。時間があれば、2番目の問題も解決したいと思います。ところで、あなたのソリューションは素晴らしいですが、明らかに非常に複雑です。
iconoclast

@iconoclas、シェル名を確実に取得できると仮定すると(コマンド置換と変数割り当ての構文はシェルによって異なることに注意)、case $shell in sh)...(Bourneのみの構文)やswitch ($shell)(cshのみ)のようなことはできません。test "$shell" = "sh" && do-something働くcsh/ sh/ rc/ esではなくfish...
ステファンChazelas

はい、私はすでにこれに遭遇しました。最悪の場合、fishは構文が不適切なスクリプトもロードしないので、STDERRをに送信することはできません/dev/null。しかし、私には解決策があり、すべてが機能するようになったら投稿します。
iconoclast

1

このようなものを試してください

shell_bin=$(ps h -p $$ -o args='' | cut -f1 -d' ')
echo $shell_bin

これは、中に失敗cshしてtcsh
iconoclast 2015

小さな変更を加えるとfish、で機能しますがfish、残念ながらでのみ機能しますps h -p %self -o args='' | cut -f1 -d' '。これは、鶏と卵の問題を引き起こします...コードfishfishバージョンを実行するには、自分がいることを知っている必要があります...
iconoclast

@ iconoclast set shell_bin=`ps -p $$ -o args='' | cut -f1 -d' '`--CSHで機能しますが、その他では機能しません
DarkHeart

@DarkHeart:実際に失敗し、変数の割り当てが上問題ではなかったので、csh/ tcsh、それはこの部分だった:ps -p $$ -o args='' | cut -f1 -d' '、その戻り-shcsh、そして-cshtcsh
iconoclast 2015

1

私は信じてあなたがすることはできません常に現在のシェルの名前を取得し、私はあなたが何ができるかの制限に注意すべきだと思います。

Linuxディストリビューションでは、ほとんどのユーザーがbash ログインと対話型シェルを持っています(bashほとんどのディストリビューションではデフォルトのシェルであるため)。一部のユーザーが自分の殻を設定しますzshcsh(および変異体)またはにfish

(他のコメントと回答は確実にアウトシェルを見つけ、説明したようbashzshtcshfishすでに挑戦しています)

Lispのインタプリタ、 -しかし、いくつかの奇妙なユーザーは、全く別の何かに自分のログインシェルを設定することscshesなど、いくつかのスクリプト・ラ・パイソン、またはOCamlのà言語、またはPerlの...-、そうする彼らの自由です。おそらく、自分のシェルをコーディングしてインタラクティブに使用している人もいるでしょう。それらの奇妙なシェルを見つけたとしても、それを利用して何かを行うことはできません(したがって、シェルの名前を取得しようとするべきではないと私は信じています)。

そのため、ソフトウェアを構成するために、ソースファイル(おそらくファイルを生成)をコーディングしていると思います。だからあなたが何をしているのか、そしてbash(そしておそらくzshtcsh)の一般的なケースのコードを説明してください...


-2

自己責任で以下を使用

tmp=`head -n 1 $0`
SHELL_BIN=`readlink -f ${tmp#*!}`
SHELL=${SHELL_BIN##*/}

それは私に空白行を与えることだ...しかし、私はあなたが、それは非常に賢いですやるしようとしているものを理解していれば
アイコノクラスト

-fオプションがない限り、空白行は表示されませんreadlink。一部の実装にはそのスイッチがありません
gwillie

私はOS Xを使用しており、manページには、引数として-f使用可能なオプションがリストさformatれています。
iconoclast 2015

$ 0がスクリプト(#!/ ...で始まる)の場合、これはいくつかの特殊なケースで機能しますが、通常の設定ではありません。ここで、ほとんどの$ 0はELFファイルを指します。

readlink私のpuredarwin vmで作業していないので、何が起こっているのかわかりません。
gwillie 2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.