.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_COMMAND
EOF
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
,您有在不同点调用的钩子函数,其灵感来自(并改进)类似的名称特殊别名在tcsh
。precmd()
是您要定义的函数,以便在每个提示之前调用它。在 中zsh
,您还可以使用precmd_functions
要调用的额外函数来设置特殊数组(errexit
也会影响那里的处理)。
nounset
¹如果在处理过程中遇到语法错误或任何类型的致命错误,该选项也可能会发生同样的情况。