[ -z "$PS1" ] && return
sshb() {
scp ~/.bashrc ${1}:
ssh $1
}
# the rest of the .bashrc
alias c=cat
...
هذه طريقة ساذجة للغاية مع العديد من العيوب الواضحة:
- يمكنك استبدال ملف .bashrc موجود
- بدلاً من اتصال واحد ، أنشأنا 2
- نتيجة لذلك ، سيتعين عليك أيضًا تسجيل الدخول مرتين.
- يمكن أن تكون وسيطة الدالة عنوان الجهاز البعيد فقط
خيار محسن:
[ -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
...
الآن نحن نستخدم اتصال واحد فقط من خلال مضاعفة الإرسال. يتم نسخ .bashrc إلى ملف لا يستخدمه bash افتراضيًا ونحدده صراحةً عبر الخيار --rcfile. لا يمكن أن تكون وسيطة الوظيفة عنوان الجهاز البعيد فحسب ، بل يمكن أن تكون أيضًا خيارات ssh الأخرى.
من حيث المبدأ ، يمكن للمرء أن يتوقف عند هذا الحد ، لكن الحل الناتج له عيب مزعج. إذا قمت بتشغيل screen أو tmux ، فسيتم استخدام .bashrc على الجهاز البعيد وستفقد جميع الأسماء المستعارة والوظائف الخاصة بك. لحسن الحظ ، يمكن التغلب على هذا. للقيام بذلك ، نحتاج إلى إنشاء برنامج نصي مجمّع ، والذي سنعلنه على أنه غلافنا الجديد. دعنا نفترض أن لدينا بالفعل برنامج نصي مغلّف على الجهاز البعيد وموجود في ~ / bin / bash-ssh. يبدو النص كما يلي:
#!/bin/bash
exec /bin/bash --rcfile ~/.bash-ssh “$@”
و .باشرك مثل هذا:
[ -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
...
إذا كان المتغير SSH_TTY موجودًا ، فإننا نتفهم أننا على الجهاز البعيد ونتجاوز متغير SHELL. من هذه اللحظة فصاعدًا ، عندما يتم إطلاق صدفة تفاعلية جديدة ، سيتم إطلاق برنامج نصي سيبدأ bash بتكوين غير قياسي محفوظ عند إنشاء جلسة ssh.
للحصول على حل عمل مناسب ، يبقى معرفة كيفية إنشاء برنامج نصي مجمّع على جهاز بعيد. من حيث المبدأ ، يمكنك إنشاؤه في تهيئة bash التي نحفظها على النحو التالي:
[ -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
}
لكن في الواقع ، يمكنك الحصول على ملف ~ / .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
...
الآن الملف ~ / .bash-ssh عبارة عن نص برمجي مستقل وتكوين bash. إنه يعمل مثل هذا. على الجهاز المحلي ، يتم تجاهل الأوامر بعد [-n "$ SSH_TTY"]. على الجهاز البعيد ، تقوم وظيفة sshb بإنشاء ملف ~ / .bash-ssh وتستخدمه كتكوين لبدء جلسة تفاعلية. تسمح لك البنية ["$ {BASH_SOURCE [0]}" == "$ {0}"] بتحديد ما إذا كان الملف قد تم تحميله بواسطة برنامج نصي آخر أو تشغيله كنص برمجي مستقل. نتيجة لذلك ، عند استخدام ~ / .bash-ssh
- كما التكوين - يتم تجاهل exec
- كبرنامج نصي - ينتقل التحكم إلى bash وينتهي تنفيذ ~ / .bash-ssh مع exec.
الآن ، عند الاتصال عبر ssh ، ستبدو بيئتك كما هي في كل مكان. يعد العمل بهذه الطريقة أكثر ملاءمة ، لكن تاريخ تنفيذ الأوامر سيبقى على الأجهزة التي تتصل بها. أنا شخصياً أرغب في حفظ السجل محليًا حتى أتمكن من التعرف على ما قمت به بالضبط على بعض الأجهزة في الماضي. للقيام بذلك ، نحتاج إلى المكونات التالية:
- خادم Tcp على الجهاز المحلي الذي سيستقبل البيانات من المقبس ويعيد توجيهها إلى ملف
- قم بإعادة توجيه منفذ الاستماع لهذا الخادم إلى الجهاز الذي نتصل به عبر ssh
- PROMPT_COMMAND في إعدادات bash ، والتي من شأنها إرسال تحديث السجل إلى المنفذ المعاد توجيهه عند إكمال الأمر
يمكن القيام بذلك على النحو التالي:
#!/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
...
تعمل الكتلة بعد [-z "$ SSH_TTY"] فقط على الجهاز المحلي. نتحقق مما إذا كان المنفذ مشغولاً وإذا لم يكن كذلك ، فقم بتشغيل netcat عليه ، حيث يتم إعادة توجيه مخرجاته إلى ملف.
يتم استدعاء وظيفة update_eternal_history قبل عرض موجه bash مباشرة. تتحقق هذه الوظيفة مما إذا كان الأمر الأخير مكررًا وإذا لم يكن الأمر كذلك ، ترسله إلى المنفذ المعاد توجيهه. إذا لم يتم تكوين المنفذ (في حالة وجود جهاز محلي) أو إذا حدث خطأ أثناء الإرسال ، ينتقل الحفظ إلى ملف محلي.
تم استكمال وظيفة sshb عن طريق تعيين إعادة توجيه المنفذ وإنشاء ارتباط رمزي يستخدمه update_eternal_history لإرسال البيانات إلى الخادم.
هذا الحل لا يخلو من عيوبه:
- المنفذ الخاص بـ netcat مشفر ، هناك فرصة للتعارض
- ( - - ), , ,
يمكن مشاهدة ملفات .bashrc الخاصة بي هنا .
إذا كانت لديك أفكار حول كيفية تحسين الحل المقترح ، يرجى المشاركة في التعليقات.
تحديث. في ubuntu 16.04 ، واجهت مشكلة: يتجمد netcat على اتصالات متعددة ويستهلك وحدة المعالجة المركزية بنسبة 100٪. لقد تحولت إلى socat ، أظهر الاختبار الأولي أن كل شيء على ما يرام. تمت إضافة منطق لإدارة الارتباط الرمزي ، والذي يحدد العنوان الذي يتم إرسال السجل إليه. اتضح مثل هذا:
#!/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
...