警告购买者:我在 Ubuntu 10.04 上使用 gnome-terminal 执行此操作,但它应该可以在任何运行zsh
.
我也稍微改变了一下。我没有混合使用“pwd”和“cwd”,而是到处使用“pwd”。
记录当前工作目录
如果您想每次都运行一个函数cd
,首选的方法是使用该chpwd
函数或更可扩展的chpwd_functions
数组。我更喜欢chpwd_functions
,因为您可以从中动态附加和删除函数。
# Records $PWD to file
function +record_pwd {
echo "$(pwd)" > ~/.pwd
}
# Removes the PWD record file
function +clean_up_pwd_record {
rm -f ~/.pwd
}
# Adds +record_pwd to the list of functions executed when "cd" is called
# and records the present directory
function start_recording_pwd {
if [[ -z $chpwd_functions[(r)+record_pwd] ]]; then
chpwd_functions=(${chpwd_functions[@]} "+record_pwd")
fi
+record_pwd
}
# Removes +record_pwd from the list of functions executed when "cd" is called
# and cleans up the record file
function stop_recording_pwd {
if [[ -n $chpwd_functions[(r)+record_pwd] ]]; then
chpwd_functions=("${(@)chpwd_functions:#+record_pwd}")
+clean_up_pwd_record
fi
}
+
在+record_pwd
and函数名称中添加 a+clean_up_pwd_record
是一种将其从正常使用中隐藏起来的黑客方式(类似地,VCS_info 挂钩通过在所有内容前加上前缀来做到这一点+vi
)。
使用上述方法,您只需start_recording_pwd
在每次更改目录时调用即可开始记录当前工作目录。同样,您可以调用stop_recording_pwd
以禁用该行为。stop_recording_pwd
还会删除~/.pwd
文件(只是为了保持清洁)。
通过这种方式,可以很容易地选择加入同步(因为您可能不希望您运行的每个 zsh 会话都这样做)。
第一次尝试:使用preexec
钩子
与@Celada 的建议类似,preexec
钩子在执行命令之前运行。这似乎是获得所需功能的一种简单方法:
autoload -Uz add-zsh-hook
function my_preexec_hook {
if [[-r ~/.pwd ]] && [[ $(pwd) != $(cat ~/.pwd) ]]; then
cd "$(cat ~/.pwd)"
fi
}
add-zsh-hook preexec my_preexec_hook
这工作......有点。由于preexec
钩子在每个命令之前运行,它会在运行下一个命令之前自动更改目录。然而,在那之前,提示停留在最后一个工作目录中,所以它为最后一个目录完成制表符,等等。(顺便说一句,空行不算作命令。)所以,它有点工作,但这并不直观。
第二次尝试:使用信号和陷阱
为了让终端自动cd
重新打印提示,事情变得更加复杂。
经过一番搜索,我发现$$
(shell的进程ID)在子shell中没有变化。因此,子外壳(或后台作业)可以轻松地向其父外壳发送信号。将此与 zsh 允许您捕获信号这一事实相结合,并且您可以~/.pwd
定期轮询:
# Used to make sure USR1 signals are not taken as synchronization signals
# unless the terminal has been told to do so
local _FOLLOWING_PWD
# Traps all USR1 signals
TRAPUSR1() {
# If following the .pwd file and we need to change
if (($+_FOLLOWING_PWD)) && [[ -r ~/.pwd ]] && [[ "$(pwd)" != "$(cat ~/.pwd)" ]]; then
# Change directories and redisplay the prompt
# (Still don't fully understand this magic combination of commands)
[[ -o zle ]] && zle -R && cd "$(cat ~/.pwd)" && precmd && zle reset-prompt 2>/dev/null
fi
}
# Sends the shell a USR1 signal every second
function +check_recorded_pwd_loop {
while true; do
kill -s USR1 "$$" 2>/dev/null
sleep 1
done
}
# PID of the disowned +check_recorded_pwd_loop job
local _POLLING_LOOP_PID
function start_following_recorded_pwd {
_FOLLOWING_PWD=1
[[ -n "$_POLLING_LOOP_PID" ]] && return
# Launch signalling loop as a disowned process
+check_recorded_pwd_loop &!
# Record the signalling loop's PID
_POLLING_LOOP_PID="$!"
}
function stop_following_recorded_pwd {
unset _FOLLOWING_PWD
[[ -z "$_POLLING_LOOP_PID" ]] && return
# Kill the background loop
kill "$_POLLING_LOOP_PID" 2>/dev/null
unset _POLLING_LOOP_PID
}
如果您调用start_following_recorded_pwd
,这将+check_recorded_pwd_loop
作为不承认的后台进程启动。这样,当您关闭 shell 时,您就不会收到烦人的“暂停作业”警告。循环的 PID 被记录(通过$!
),因此可以稍后停止。
循环只是USR1
每秒向父 shell 发送一个信号。此信号被 捕获,如有必要TRAPUSR1()
,它将重新打印提示。cd
我不明白必须同时调用zle -R
and zle reset-prompt
,但这是对我有用的神奇组合。
还有_FOLLOWING_PWD
国旗。由于每个终端都TRAPUSR1
定义了函数,因此除非您实际指定了该行为,否则这会阻止它们处理该信号(和更改目录)。
与记录当前工作目录一样,您可以调用stop_following_posted_pwd
来停止整个自动操作cd
。
将两半放在一起:
function begin_synchronize {
start_recording_pwd
start_following_recorded_pwd
}
function end_synchronize {
stop_recording_pwd
stop_following_recorded_pwd
}
最后,您可能想要这样做:
trap 'end_synchronize' EXIT
这将在您的终端退出之前自动清理所有内容,从而防止您意外留下孤立的信号循环。