我有两个运行zsh的iTerm窗口:一个用于vim中的文档;另一个我用来执行shell命令。我想同步两个会话的当前工作目录。我想我可以通过每次更改目录
时输出到新文件~/.cwd
来完成此操作
alias cd="cd; pwd > ~/.cwd"
并创建一个shell脚本~/.dirsync
,每秒监视~/.cwd
的内容,如果另一个shell更新了目录,则更改目录。
#!/bin/sh
echo $(pwd) > ~/.cwd
alias cd="cd; echo $(pwd) > ~/.cwd"
while true
do
if [[ $(pwd) != $(cat ~/.cwd) ]]
then
cd $(cat ~/.cwd)
fi
sleep 1
done
然后我会将以下代码行附加到~/.zshrc
。
~/.dirsync &
然而,它没有用。然后我发现shell脚本总是在它自己的子shell中执行。有没有人知道如何使这项工作?
答案 0 :(得分:2)
警告:我在Ubuntu 10.04上使用gnome-terminal执行此操作,但它应该在运行zsh
的任何* NIX平台上运行。
我也稍微改变了一点。而不是混合“pwd”和“cwd”,我到处都是“pwd”。
如果您希望每次cd
时都运行一项功能,首选方法是使用chpwd
function or the more extensible chpwd_functions
array。我更喜欢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
和+clean_up_pwd_record
函数名称是一种隐藏正常使用的黑客方法(类似地,VCS_info挂钩通过使用{前缀所有内容来实现此目的) {1}})。
通过上述内容,您只需在每次更改目录时调用+vi
即可开始记录当前工作目录。同样,您可以调用start_recording_pwd
来禁用该行为。 stop_recording_pwd
还会移除stop_recording_pwd
文件(只是为了保持清洁)。
通过这种方式,可以轻松地选择同步(因为您可能不希望在运行的每个zsh会话中都这样做。)
~/.pwd
挂钩与@Celada的建议类似,preexec
挂钩在执行命令之前运行。这似乎是获得所需功能的简单方法:
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中没有变化。因此,子shell(或后台作业)可以轻松地向其父级发送信号。将此与zsh允许您trap signals的事实相结合,您可以定期轮询$$
:
~/.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
作为不同的后台流程。这样,当您关闭shell时,您将不会收到恼人的“暂停作业”警告。记录循环的PID(通过+check_recorded_pwd_loop
),以便稍后停止。
循环每秒向父shell发送一个$!
信号。此信号被USR1
捕获,TRAPUSR1()
将在必要时重新打印提示。我不明白必须同时调用cd
和zle -R
,但这是对我有用的神奇组合。
还有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
}
这将在您的终端退出之前自动清理所有内容,从而防止您意外地离开孤立的信号环路。