バッシュマニュアル

バッシュマニュアル

.bash_historyPROMPT_COMMAND次の設定を使用して、すべてのターミナルタブとウィンドウでコマンド履歴を有効にしたいと思います.profile

export PROMPT_COMMAND="history -a; history -c; history -r;$PROMPT_COMMAND"

ただし、この環境変数が設定されていることを確認すると、次の結果が表示されます。

echo $PROMPT_COMMAND
printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/\~}"

PROMPT_COMMANDこのようにエクスポートすると既存のリストが上書きされますか、またはエクスポートする前に値の前にプレフィックスを付ける必要が$PROMPT_COMMANDありますか?PROMPT_COMMAND:

答え1

Google検索でのトップ結果の1つであるこの"PROMPT_COMMAND seperator"回答は、これまでに提供されたものよりも多くの努力を払う価値があると思います。

メモ:この投稿を準備しながら、Bashバージョン3.2、4.4、5.0、および5.1をテストしました。ドッカー)同じバージョンのソースコードを確認しました。gnu.org)

始めましょう...


バッシュマニュアル

バッシュ<= v5.0

PROMPT_COMMAND変数の値は以前にチェックされます。
Bashは各キープロンプトを印刷します。 PROMPT_COMMANDが設定されている
NULL以外の値がある場合は、NULL以外のように値を実行します。
すでにコマンドラインに入力されています。

どういう意味ですか?

言い換えれば、bashは次のようにほぼ同じことを行います。

eval "${PROMPT_COMMAND:-}"

したがって、この目的に適したコマンドの接続/順序はeval許可されます。

バッシュ>= v5.1

Bash は配列変数 PROMPT_COMMAND の値をチェックします。
各キーチップを印刷する直前。

PROMPT_COMMANDの要素が設定されていて空でない場合
すでに実行されているように、各値を番号順に実行します。
コマンドラインに入力してください。
...
この変数が設定されて配列の場合、各変数の値は
set 要素は実行されるコマンドとして解釈されます。
メインプロンプト($ PS1)を印刷する前。

設定されているが配列変数ではない場合、その値が使用されます。
実行されるコマンドとして。

どういう意味ですか?

言い換えれば、bashは次のようにほぼ同じことを行います。

if [[ ${#PROMPT_COMMAND[@]} -gt 1 ]]; then
    for cmd in "${PROMPT_COMMAND[@]}"; do eval "${cmd:-}"; done
else
    eval "${PROMPT_COMMAND:-}"
fi

つまり、配列の各要素はeval独立しています。

コミュニティは何をしていますか?

検索"PROMPT_COMMAND"githubに表示されます:

  • 通常、コミュニティでは;区切り文字として使用されます。
  • 場合によっては$'\n'区切り文字として使用されます

具体的に見たことは

いくつかの逸話の注意:

実際に働く様子を見てみましょう

まず、新しいBash v3.2環境を構築しましょう。

docker run -it --rm bash:3.2
bash-3.2$ 

セミコロンで区切られた文字列

- で区切られた文字列を使用してテストすると、;次のようになります。

bash-3.2$ PROMPT_COMMAND='printf 1;printf 2;printf 3;printf ":\n"'
123:
bash-3.2$ 

行で区切られた文字列

- で区切られた文字列を使用してこの結果をテストします\n

bash-3.2$ PROMPT_COMMAND=$'printf 1\nprintf 2\nprintf 3\nprintf ":\n"'
123:
bash-3.2$ 

両方混ぜる

両方の区切り記号を混在させることはできますか?うん! :

bash-3.2$ PROMPT_COMMAND=$'printf 1;printf 2\nprintf 3;printf ":\n"'
123:
bash-3.2$ 

配列はどうですか?

私たちが行くところでは、Bash v5.1が必要です。

docker run -it --rm bash:5.1
bash-5.1$ 

リテラル配列

リテラル配列を使用してこの結果をテストします。

bash-5.1$ PROMPT_COMMAND=( "printf 1" "printf 2" "printf 3" "printf ':\n'" )
123:
bash-5.1$ 
3つすべて試してみてください!

みんな混ぜるとどうなりますか?

bash-5.1$ PROMPT_COMMAND=( "printf 1" $'printf 2\nprintf 3' 'printf 4;printf ":\n"' )
1234:
bash-5.1$ 

どちらを使用しますか?

${PROMPT_COMMAND}配列の場合は、配列にコマンドを追加/追加する必要があります。

(注)必要に応じて、コマンドグループ(単一の文字列や区切り文字列など;$'\n'を配列に個別の項目として追加できます。

${PROMPT_COMMAND}配列でない場合は、次のことを行う必要があります。おそらく;より正式な区切り記号のように感じるので、コマンドを追加するときに使用してください。ただし、追加するコマンドは、$'\n「.」で区切られたステップを含むことができるより複雑なスクリプトである可能性があることに注意してください。

咀嚼機能を試してみますか?

確かに!

##
# pc_munge munges PROMPT_COMMAND.
# Tries to accommodate when PROMPT_COMMAND is an array (supported in Bash v5.1+).
# If ${#PROMPT_COMMAND[@]} has 2+ elements, then we treat as an array, otherwise
# we defensively treat it as a string.
# By default, uses ';' as separator.
#
# NOTE: Does NOT check if command is already present
#
# Parms:
#  $1 command to add
#  $2 before | after (default: after)
#  $3 separator (default: ';')
#
pc_munge() {
    [[ -n "$1" ]] || return
    local fs="${3:-;}" # null | '' => default
    case "${2:-after}" in
        before)
            [[ ${#PROMPT_COMMAND[@]} -gt 1 ]] && PROMPT_COMMAND=( "$1" "${PROMPT_COMMAND[@]}" ) || PROMPT_COMMAND="${1}${PROMPT_COMMAND:+${fs}$PROMPT_COMMAND}"
            ;;
        *) # after
            [[ ${#PROMPT_COMMAND[@]} -gt 1 ]] && PROMPT_COMMAND+=( "$1" ) || PROMPT_COMMAND="${PROMPT_COMMAND:+$PROMPT_COMMAND${fs}}${1}"
            ;;
    esac
}

[編集: スペル エラー "PROMP_" => "PROMPT_"]

[編集: 太字"Som" => "一部"]

[編集:"歌う"スペルエラー=>"シングル"]

答え2

$ PROMPT_COMMANDはコロンで区切られたリストですか?

テストするのは簡単です。

$ PROMPT_COMMAND='true:true' bash 
bash: true:true: command not found
$ exit

したがって、答えは「いいえ」です。

しかし、あなたはそれを次のように考えることができますセミコロン他のシェルコード行と同様に、区切られたコマンドシーケンス:

$ PROMPT_COMMAND='echo x;echo y' bash 
x
y
$ exit

あなたの質問のタスクにはセミコロンで区切られたいくつかのコマンドがあり、PROMPT_COMMAND最後に以前の値が追加されています。

もちろん、PROMPT_COMMAND複数のコマンドを実行する別の方法は、関数を作成してそこから呼び出すことです。

つまり、2つの理由で、printfシーケンスが実際のプロンプトでよりよく配置されているように見えます。PROMPT_COMMANDまず、改行文字で終わらないので、終了する前に不完全な行を出力する他のコマンドのように、カーソル位置に関するBashの理解を台無しにすることができます。第二に、タブの完成を介してシェルにプロンプ​​トを再印刷するように要求した場合、PS1プロンプトは再び表示されますが、PROMPT_COMMAND再実行されません。

答え3

bash $PROMPT_COMMAND(または@DavidFarrellがすでに指摘したようにbash 5.1 +の配列の場合は各要素)はシェルコードとして解釈されるため、その内容をシェルスクリプトのように書くことができます。シェルスクリプトでは、コマンドは、、、、、;などnewlineで区切る|ことができます。各コマンドは独自の意味を持ちます。&&&||

たとえば、次のようになります。

PROMPT_COMMAND='
  cmd1 && cmd2 &
  cmd3; cmd4
  for cmd in cmd5 cmd6; do
    "$cmd"
  done
  cmd7 << "EOF"
foo bar
EOF'

など。

すでに設定されているコマンドにコマンドを追加し、$PROMPT_COMMAND前のコマンドとは無関係に実行するには、次を選択します;新しいチームただし、最初に空の場合、またはこの文書区切り文字(上記のように)で終わる特別な場合は機能しません;$PROMPT_COMMANDEOF

PROMPT_COMMAND+='
  your extra command here'

(または以前のバージョンの場合:

PROMPT_COMMAND=$PROMPT_COMMAND'
  your extra command here'

)

優先されます。または:

PROMPT_COMMAND='your extra command here
'$PROMPT_COMMAND

まず実行するように事前に追加してください。

Bash 5.1+では、配列要素を追加することもできます。

PROMPT_COMMAND+=(
  'your extra command here'
)

または前に追加してください:

PROMPT_COMMAND=(
  'your extra command here'
  "${PROMPT_COMMAND[@]}"
)

これは潜在的な問題を残します。errexit-e)オプションが有効になっていて、これらのスクリプトのコマンドが失敗すると処理が中止されるため、追加のコマンドが実行されないことがあります。しかし、これはおそらく病的な例として見なければなりません。おそらくインタラクティブシェルで使用するのはerrexit悪い考えです(スクリプトで使用されている場合はすでに議論の余地があります)。

比較のために、zsh代わりに$PROMPT_COMMAND同様の名前からインスピレーションを受けた(および改善された)別のポイントから呼び出されるフック関数があります。特別なエイリアス存在するtcshprecmd()各プロンプトの前に呼び出されるように定義したい関数。また、呼び出す追加の関数を含む特別な配列を設定することもzshできます(処理にも影響します)。precmd_functionserrexit


nounset¹処理中に構文エラーまたは致命的なエラーが発生した場合、このオプションでも同じ現象が発生する可能性があります。

関連情報