シェルプロンプトにコマンドが与えられたかどうかを検出する方法

シェルプロンプトにコマンドが与えられたかどうかを検出する方法

PROMPT_COMMAND以前にコマンドプロンプトで提供されたすべての項目に応答するコードを書きたいです。たとえば、広範で情報豊富なプロンプトと簡単で簡潔なプロンプトを切り替えるには、次のようにします。

mikemol@serenity ~ $ echo hi
hi
$ echo ho
ho
$ echo hum
hum
$
mikemol@serenity ~ $

空でない場合にのみシェルの履歴に値が追加されるため、最新の履歴エントリが空であることを単にテストすることはできません。set | grep some_command実行後に実行しsome_commandても結果が出なかったので、その情報を含む環境変数がないようです。

を主に使用するのにbash、POSIX互換ソリューションと他のシェルが気になります。

答え1

PROMPT_COMMAND結局は全く必要なくなりました。正しい方向を教えてくれたクリストファーに感謝します。

代わりに、次のファイルを検討してくださいps1.prompt

${__cmdnbary[\#]+$(
    echo '\u@\h: \w' # Your fancy prompt goes here, with all the usual special characters available.
) }${__cmdnbary[\#]=}\$

その後、これを私の項目に入力できますPS1

PS1=$(cat ps1.prompt)

(これを行う必要はありませんが、イラストや編集に便利だと思います。)

だから私たちは以下を見ます:

mikemol@zoe:~$ echo hi
hi
mikemol@zoe:~$ echo ho
ho
mikemol@zoe:~$ echo hum
hum
mikemol@zoe:~$ 
mikemol@zoe:~$ PS1=$(cat ps1.prompt)
$ 
mikemol@zoe: ~ $ echo hi
hi
$ echo ho
ho
$ echo hum
hum
$ 
mikemol@zoe: ~ $ 

私達は配列核を使用していますここに表示bashただし、パラメータ置換を使用するのではなく、以前のコマンドが実行されていない場合にのみトリガする方法を${parameter:-word}使用します。${parameter+word}

ロジックで二重否定を使用する必要があるため、これについての説明が必要です。

${__cmdnbary[\#]-word}${__cmdnbary[\#]=}動作原理

元の配列クラッキングデモでは、${__cmdnbary[\#]-word}${__cmdnbary[\#]=}この構成が使用されました。 (明確さのために$?交換しましたword)。パラメータの拡張と配列に特に慣れていない場合(私は慣れていない)、何が起こっているのかは全くわかりません。

まず、理解する\#

すべて手動:

\#コマンドのコマンド番号

...

コマンド番号は、現在のシェルセッション中に実行されるコマンドの順序の位置です。

これは\#変更のみを意味します。そして、もし注文を実行します。ユーザーがプロンプトに空行を入力すると、コマンドは実行されないため、\#変更は適用されません。

${__cmdnbary[#]=}空文字列の設定

${__cmdnbary[\#]=}パラメータ拡張を使用します。に戻る手動:

${parameter:=word}

デフォルト値の指定。引数が設定されていないか空の場合、単語の拡張が引数に割り当てられます。次に、パラメータの値を置き換えます。

したがって、__cmdnbary[\#]設定されていないかヌルの場合、この構文は空の文字列(この場合は空の文字列)を割り当て、構文全体wordは出力内の同じ空の文字列に置き換えられます。

__cmdnbary[\#]〜するいつも最初に見ると、#は単調なので設定されていないかnullになります。常に増加または同じままです。 (つまり、繰り返されるまで、おそらく2^31または2^63程度になりますが、他の問題が発生します。長い私達がそこに着く前に。私がこのソリューションを一種のハッキングであると説明する理由があります。 )

条件式は次のとおりです。${__cmdnbary[\#]-word}

${__cmdnbary[\#]-word}別のパラメータ拡張です。マニュアルから:

${parameter:-word}

デフォルト値の使用。引数が設定されていないか空の場合、単語の拡張が置き換えられます。それ以外の場合、パラメータ値は置き換えられます。

したがって、配列項目が次のような\#場合設定されていないか空wordその場で使用されます。確認する前に割り当てを試みないため__cmdnbary[\#](代わりに使用${parameter:=word})、指定された値を最初に確認したときに\#配列の対応する位置が設定されていないことを確認する必要があります。

bashスパース配列の使用

Cスタイルの配列に慣れているユーザーには少し説明が必要です。bash実際に使用まれな配列;割り当てられるまで設定されていない配列の位置に移動します。空の文字列は「null または unset」と同じではありません。

代わりに ${__cmdnbary[#]+word}${__cmdnbary[#]=} を使用するのはなぜですか?

${__cmdnbary[\#]+word}${__cmdnbary[\#]=}そして${__cmdnbary[#]-word}${__cmdnbary[#]=} look very siilar. The *only* thing we change between the two constructs can be found in the first portion; we use${parameter:+word} instead of${parameter:-word}`。

${parameter:-word}これはnullまたは設定されていない場合にのみ表示されますword。私たちの場合は、次の場合にのみ表示されます。parameterいいえ配列の位置を設定します。\#増加した場合にのみこれを行いません。これはコマンドを実行した場合にのみ発生します。

これは、次の場合${parameter:-word}にのみレンダリングすることを意味します。wordいいえ私たちが望むものと正反対のコマンドが実行されます。だから我々は${parameter:-word}それを代わりに使用します。また、マニュアルで:

${parameter:+word}

代替値の使用。引数が空であるか設定されていない場合は何も置き換えられず、そうでない場合は単語の拡張が置き換えられます。

これは(残念ながら)理解すべき二重否定論理ですが、そこにあります。

プロンプト自体

移行メカニズムについて説明しましたが、プロンプト自体はどうですか?

$( ... )これはプロンプトの内容を含めるために使用されます。主に自分自身の読みやすさのためにそうする必要はありません。$( ... )通常、変数の割り当てに記入するものに置き換えることができます。

なぜハッキングですか?

希少な配列にアイテムを追加した方法を覚えていますか?これらの項目は削除しないため、シェルセッションを終了するまで配列は永久に大きくなりますPS1。私が知る限り方法はありません未設定プロンプトの変数または配列の位置。試してみることもできますが、うまく$()いかないことがわかります。サブシェル内の変数名前空間への変更は、派生したサブシェルのスペースには適用されません。

あなた可能割り当て前および結果ファイルの内容情報をmktmp最初に試してください。その後、現在のコンテンツを保存されたコンテンツと比較できますが、プロンプトは緊急時にディスクI / Oに依存します。あなた自身。.bashrcPS1\#

関連情報