Bash 手册

Bash 手册

.bash_history我想通过以下设置PROMPT_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前加上 a前缀?:

答案1

作为搜索时谷歌上的最高结果之一"PROMPT_COMMAND seperator",我觉得这个答案值得比迄今为止给出的更多努力。

笔记:为了准备这篇文章,我测试了 Bash 版本 3.2、4.4、5.0 和 5.1(通过泊坞窗)并查看了相同版本的源代码(通过gnu.org

让我们开始 ...


Bash 手册

bash <= v5.0

变量 PROMPT_COMMAND 的值在之前被检查
Bash 打印每个主要提示符。如果设置了 PROMPT_COMMAND 并且
具有非空值,则执行该值就像它一样
已在命令行上输入。

这是什么意思?

这意味着 bash 的作用大致相当于:

eval "${PROMPT_COMMAND:-}"

因此,任何适用于此目的的命令链接/排序eval都是允许的。

重击 >= v5.1

Bash 检查数组变量 PROMPT_COMMAND 的值
就在打印每个主要提示之前。

如果 PROMPT_COMMAND 中的任何元素已设置且非空,Bash
按数字顺序执行每个值,就好像它已被执行一样
在命令行中输入。
...
如果设置了此变量并且是一个数组,则每个变量的值
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$ 
使用全部三个!

如果你把它们全部混合起来会怎么样:

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

所以,答案是“不”。

但你可以把它当作分号分隔的命令序列,与任何其他 shell 代码行一样:

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

这就是您的问题中的作业所具有的内容:许多命令,以分号分隔,并将较早的值PROMPT_COMMAND附加到末尾。

当然,从PROMPT_COMMAND, 运行多个命令的另一种方法是创建一个函数并从那里调用它。

也就是说,printf您的序列看起来PROMPT_COMMAND可能更好地放置在实际提示中,原因有两个。首先,它不会以换行符结尾,因此它可能会扰乱 Bash 对光标位置的理解,就像其他在退出前输出不完整行的命令一样。其次,如果你让 shell 重新打印提示符,通过制表符补全,PS1将会重新显示,但PROMPT_COMMAND不会再次运行。

答案3

bash 的$PROMPT_COMMAND(或者每个元素,如果它是 bash 5.1+ 的数组,正如@DavidFarrell 已经指出的那样)被解释为 shell 代码,因此您可以像编写 shell 脚本一样编写其内容。在 shell 脚本中,命令可以用;, newline, |, &, &&,等分隔||,每个命令都有自己的含义。

所以你可以有例如:

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

ETC。

要将命令附加到已设置的命令$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) 选项,并且这些脚本中的任何命令失败,处理就会在那里停止,因此您的额外命令可能最终不会运行。不过,这可能应该被视为一个病态案例。在交互式 shell 中使用errexit可能是一个坏主意(在脚本中使用时已经相当有争议)。

为了进行比较,在 中zsh,而不是 中$PROMPT_COMMAND,您有在不同点调用的钩子函数,其灵感来自(并改进)类似的名称特殊别名tcshprecmd()是您要定义的函数,以便在每个提示之前调用它。在 中zsh,您还可以使用precmd_functions要调用的额外函数来设置特殊数组(errexit也会影响那里的处理)。


nounset¹如果在处理过程中遇到语法错误或任何类型的致命错误,该选项也可能会发生同样的情况。

相关内容