警告エンプター:これは、Ubuntu 10.04でgnome-terminalを使用して実行していますが、を実行している*NIXプラットフォームで動作するはずです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
}
関数名にa+
を追加することは、通常の使用からそれを隠すためのハックっぽい方法です(同様に、VCS_infoフックはすべてに接頭辞を付けることによってこれを行います)。+record_pwd
+clean_up_pwd_record
+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
、次のコマンドを実行する前にディレクトリが自動的に変更されます。ただし、それまでは、プロンプトは最後の作業ディレクトリにとどまるため、最後のディレクトリなどのタブが完成します(ちなみに、空白行はコマンドとしてカウントされません)。しかし、それは直感的ではありません。
2番目の試み:信号とトラップの使用
端末に自動的cd
にプロンプトを再印刷させるために、事態はさらに複雑になりました。
いくつか検索したところ、$$
(シェルのプロセスID)がサブシェルで変更されていないことがわかりました。したがって、サブシェル(またはバックグラウンドジョブ)は、その親にシグナルを簡単に送信できます。これを、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
このようにして、シェルを閉じようとしたときに、迷惑な「ジョブの一時停止」の警告が表示されることはありません。ループのPIDは(を介して$!
)記録されるため、後で停止できます。
ループは、親シェルにUSR1
1秒ごとにシグナルを送信するだけです。この信号はによってトラップされ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
}
最後に、おそらくこれを実行する必要があります。
trap 'end_synchronize' EXIT
これにより、端末が終了する直前にすべてが自動的にクリーンアップされるため、孤立したシグナリングループが誤って残されるのを防ぐことができます。