[ -z "$PS1" ] && return
sshb() {
scp ~/.bashrc ${1}:
ssh $1
}
# the rest of the .bashrc
alias c=cat
...
Ini adalah cara yang sangat naif dengan beberapa kelemahan yang jelas:
- Anda dapat menimpa .bashrc yang sudah ada
- Alih-alih satu koneksi, kami membuat 2
- Akibatnya, Anda juga harus masuk 2 kali.
- Argumen fungsi hanya dapat berupa alamat mesin jarak jauh
Opsi yang ditingkatkan:
[ -z "$PS1" ] && return
sshb() {
local ssh="ssh -S ~/.ssh/control-socket-$(tr -cd '[:alnum:]' < /dev/urandom|head -c8)"
$ssh -fNM "$@"
$ssh placeholder "cat >~/.bash-ssh" <~/.bashrc
$ssh "$@" -t "bash --rcfile ~/.bash-ssh -i"
$ssh placeholder -O exit >/dev/null 2>&1
}
# the rest of the .bashrc
alias c=cat
...
Sekarang kami hanya menggunakan satu koneksi melalui multiplexing. .bashrc disalin ke file yang tidak digunakan oleh bash secara default dan kami secara eksplisit menentukannya melalui opsi --rcfile. Argumen fungsi tidak hanya dapat berupa alamat mesin jarak jauh, tetapi juga opsi ssh lainnya.
Pada prinsipnya, seseorang dapat berhenti di sini, tetapi solusi yang dihasilkan memiliki kekurangan yang tidak menyenangkan. Jika Anda menjalankan screen atau tmux, .bashrc pada mesin jarak jauh akan digunakan dan semua alias dan fungsi Anda akan hilang. Untungnya, hal ini bisa diatasi. Untuk melakukan ini, kita perlu membuat skrip pembungkus, yang akan kita deklarasikan sebagai shell baru kita. Mari kita asumsikan untuk kesederhanaan bahwa kita sudah memiliki skrip pembungkus pada mesin jarak jauh dan terletak di ~ / bin / bash-ssh. Skripnya terlihat seperti ini:
#!/bin/bash
exec /bin/bash --rcfile ~/.bash-ssh “$@”
Dan .bashrc seperti ini:
[ -n "$SSH_TTY" ] && export SHELL="$HOME/bin/bash-ssh"
[ -z "$PS1" ] && return
sshb() {
local ssh="ssh -S ~/.ssh/control-socket-$(tr -cd '[:alnum:]' < /dev/urandom|head -c8)"
$ssh -fNM "$@"
$ssh placeholder "cat >~/.bash-ssh" <~/.bashrc
$ssh "$@" -t "bash --rcfile ~/.bash-ssh -i"
$ssh placeholder -O exit >/dev/null 2>&1
}
# the rest of the .bashrc
alias c=cat
...
Jika variabel SSH_TTY ada, kami memahami bahwa kami berada di mesin jarak jauh dan mengganti variabel SHELL. Mulai saat ini, ketika shell interaktif baru diluncurkan, skrip akan diluncurkan yang akan memulai bash dengan konfigurasi non-standar yang disimpan saat sesi ssh dibuat.
Untuk mendapatkan solusi kerja yang nyaman, tetap mencari cara untuk membuat skrip pembungkus pada mesin jarak jauh. Pada prinsipnya, Anda dapat membuatnya di konfigurasi bash yang kami simpan seperti ini:
[ -n "$SSH_TTY" ] && {
mkdir -p "$HOME/bin"
export SHELL="$HOME/bin/bash-ssh"
echo -e '#!/bin/bash\nexec /bin/bash --rcfile ~/.bash-ssh "$@"' >$SHELL
chmod +x $SHELL
}
Namun nyatanya, Anda bisa bertahan dengan satu file ~ / .bash-ssh:
#!/bin/bash
[ -n "$SSH_TTY" ] && [ "${BASH_SOURCE[0]}" == "${0}" ] && exec bash --rcfile "$SHELL" "$@"
[ -z "$PS1" ] && return
sshb() {
local ssh="ssh -S ~/.ssh/control-socket-$(tr -cd '[:alnum:]' < /dev/urandom|head -c8)"
$ssh -fNM "$@"
$ssh placeholder "cat >~/.bash-ssh" <~/.bashrc
$ssh "$@" -t 'SHELL=~/.bash-ssh; chmod +x $SHELL; bash --rcfile $SHELL -i'
$ssh placeholder -O exit >/dev/null 2>&1
}
# the rest of the .bashrc
alias c=cat
...
Sekarang file ~ / .bash-ssh adalah skrip mandiri dan konfigurasi bash. Cara kerjanya seperti ini. Di komputer lokal, perintah setelah [-n "$ SSH_TTY"] diabaikan. Pada mesin jarak jauh, fungsi sshb membuat file ~ / .bash-ssh dan menggunakannya sebagai konfigurasi untuk memulai sesi interaktif. Konstruksi ["$ {BASH_SOURCE [0]}" == "$ {0}"] memungkinkan Anda untuk menentukan apakah sebuah file diunggah oleh skrip lain atau diluncurkan sebagai skrip mandiri. Akibatnya, ketika ~ / .bash-ssh digunakan
- sebagai config - exec diabaikan
- sebagai skrip - kontrol lolos ke bash dan eksekusi ~ / .bash-ssh diakhiri dengan exec.
Sekarang, saat menghubungkan melalui ssh, lingkungan Anda akan terlihat sama di mana-mana. Jauh lebih mudah bekerja dengan cara ini, tetapi riwayat eksekusi perintah akan tetap ada di mesin yang Anda sambungkan. Secara pribadi, saya ingin menyimpan sejarah secara lokal sehingga saya dapat memoles apa yang sebenarnya telah saya lakukan pada beberapa mesin di masa lalu. Untuk melakukan ini, kita membutuhkan komponen berikut:
- Server tcp di mesin lokal yang akan menerima data dari soket dan mengarahkannya ke file
- Meneruskan port pendengar dari server ini ke mesin yang kita hubungkan melalui ssh
- PROMPT_COMMAND dalam pengaturan bash, yang akan mengirim pembaruan riwayat ke port yang diteruskan setelah menyelesaikan perintah
Ini bisa dilakukan seperti ini:
#!/bin/bash
[ -n "$SSH_TTY" ] && [ "${BASH_SOURCE[0]}" == "${0}" ] && exec bash --rcfile "$SHELL" "$@"
[ -z "$PS1" ] && return
[ -z "$SSH_TTY" ] && {
history_port=26574
netstat -lnt|grep -q ":${history_port}\b" || {
umask 077 && nc -kl 127.0.0.1 "$history_port" >>~/.bash_eternal_history &
}
}
HISTSIZE=$((1024 * 1024))
HISTFILESIZE=$HISTSIZE
HISTTIMEFORMAT='%t%F %T%t'
update_eternal_history() {
local histfile_size=$(stat -c %s $HISTFILE)
history -a
((histfile_size == $(stat -c %s $HISTFILE))) && return
local history_line="${USER}\t${HOSTNAME}\t${PWD}\t$(history 1)"
local history_sink=$(readlink ~/.bash-ssh.history 2>/dev/null)
[ -n "$history_sink" ] && echo -e "$history_line" >"$history_sink" 2>/dev/null && return
local old_umask=$(umask)
umask 077
echo -e "$history_line" >> ~/.bash_eternal_history
umask $old_umask
}
[[ "$PROMPT_COMMAND" == *update_eternal_history* ]] || export PROMPT_COMMAND="update_eternal_history;$PROMPT_COMMAND"
sshb() {
local ssh="ssh -S ~/.ssh/control-socket-$(tr -cd '[:alnum:]' < /dev/urandom|head -c8)"
$ssh -fNM "$@"
local bashrc=~/.bashrc
[ -r ~/.bash-ssh ] && bashrc=~/.bash-ssh && history_port=$(basename $(readlink ~/.bash-ssh.history))
local history_remote_port="$($ssh -O forward -R 0:127.0.0.1:$history_port placeholder)"
$ssh placeholder "cat >~/.bash-ssh; ln -nsf /dev/tcp/127.0.0.1/$history_remote_port ~/.bash-ssh.history" < $bashrc
$ssh "$@" -t 'SHELL=~/.bash-ssh; chmod +x $SHELL; bash --rcfile $SHELL -i'
$ssh placeholder -O exit >/dev/null 2>&1
}
# the rest of the .bashrc
alias c=cat
...
Pemblokiran setelah [-z "$ SSH_TTY"] hanya bekerja di mesin lokal. Kami memeriksa apakah port sibuk dan jika tidak, jalankan netcat di atasnya, yang outputnya dialihkan ke file.
Fungsi update_eternal_history dipanggil tepat sebelum prompt bash ditampilkan. Fungsi ini memeriksa apakah perintah terakhir adalah duplikat dan jika tidak, mengirimkannya ke port yang diteruskan. Jika port tidak dikonfigurasi (dalam kasus mesin lokal) atau jika terjadi kesalahan saat mengirim, simpan ke file lokal.
Fungsi sshb telah dilengkapi dengan pengaturan port forwarding dan membuat symlink yang akan digunakan oleh update_eternal_history untuk mengirim data ke server.
Solusi ini bukannya tanpa kekurangan:
- Port untuk netcat di-hardcode, ada kemungkinan mengalami konflik
- ( - - ), , ,
.Bashrc saya sendiri dapat dilihat di sini .
Jika Anda memiliki ide tentang cara meningkatkan solusi yang diusulkan, silakan bagikan di komentar.
Memperbarui. Di ubuntu 16.04, saya mengalami masalah: netcat membeku pada banyak koneksi dan membutuhkan 100% cpu. Saya beralih ke socat, pengujian pendahuluan menunjukkan bahwa semuanya baik-baik saja. Ditambahkan pula logika untuk mengatur symlink, yang menentukan alamat tujuan pengiriman history. Ternyata seperti ini:
#!/bin/bash
[ -n "$SSH_TTY" ] && [ "${BASH_SOURCE[0]}" == "${0}" ] && exec bash --rcfile "$SHELL" "$@"
[ -z "$PS1" ] && return
[ -z "$SSH_TTY" ] && command -v socat >/dev/null && {
history_port=26574
netstat -lnt|grep -q ":${history_port}\b" || {
umask 077 && socat -u TCP4-LISTEN:$history_port,bind=127.0.0.1,reuseaddr,fork OPEN:$HOME/.bash_eternal_history,creat,append &
}
}
HISTSIZE=$((1024 * 1024))
HISTFILESIZE=$HISTSIZE
HISTTIMEFORMAT='%t%F %T%t'
update_eternal_history() {
local histfile_size=$(stat -c %s $HISTFILE)
history -a
((histfile_size == $(stat -c %s $HISTFILE))) && return
local history_line="${USER}\t${HOSTNAME}\t${PWD}\t$(history 1)"
local history_sink=$(readlink ~/.bash-ssh.history 2>/dev/null)
[ -n "$history_sink" ] && echo -e "$history_line" >"$history_sink" 2>/dev/null && return
local old_umask=$(umask)
umask 077
echo -e "$history_line" >> ~/.bash_eternal_history
umask $old_umask
}
[[ "$PROMPT_COMMAND" == *update_eternal_history* ]] || PROMPT_COMMAND="update_eternal_history;$PROMPT_COMMAND"
sshb() {
local ssh="ssh -S ~/.ssh/control-socket-$(tr -cd '[:alnum:]' < /dev/urandom|head -c8)"
local bashrc=~/.bashrc
local history_command="rm -f ~/.bash-ssh.history"
[ -r ~/.bash-ssh ] && bashrc=~/.bash-ssh && history_port=$(basename $(readlink ~/.bash-ssh.history 2>/dev/null))
$ssh -fNM "$@"
[ -n "$history_port" ] && {
local history_remote_port="$($ssh -O forward -R 0:127.0.0.1:$history_port placeholder)"
history_command="ln -nsf /dev/tcp/127.0.0.1/$history_remote_port ~/.bash-ssh.history"
}
$ssh placeholder "${history_command}; cat >~/.bash-ssh" < $bashrc
$ssh "$@" -t 'SHELL=~/.bash-ssh; chmod +x $SHELL; bash --rcfile $SHELL -i'
$ssh placeholder -O exit >/dev/null 2>&1
}
# the rest of the .bashrc
alias c=cat
...