One day I used to SSH to lots of machines to do some administrative tasks. I constantly used l
to list directories just like I do on my own machines. Guess what, that doesn't work. There were no any usable aliases! So I opened a code editor...
xssh
This script will connect to remote host via ssh
and source specified rc file (similar to .bashrc
). It is possible to call xssh
once again to go deeper, so one may say it is self-reproducing.
I use it for a year or so, code works fine. Things I worry about:
- fallback behaviour when there is no
bash
shell base64
is required on host system- to be used as
alias ssh=xssh
script must behave similary tossh
- overal code style and consistence
- in last
HEREDOC
I try to determine if current shell isbash
. If not, I try to fallback to normal shell (w/o~/.xssh
usage). Not sure it is done the right way.
I aim to make this code go public, so I need your review and recommendations.
#!/bin/sh
# xssh - squire for your recursive ssh adventures
# This tiny script will follow you in your ssh adventures,
# just like squires serve their knights.
# It will run commands (think .bashrc) on remote systems and
# follow while you travel from host to host no matter how far
# the road takes you.
# xssh runs ssh and sources your ~/.xssh right after successfull
# login to bash. More over xssh tries to be "self-reproducing"
# so you can use it again to ssh from remote system.
# As stated by shebang xssh doesn't require bash on host
# system but it requires one on the remote.
XSSH_RC=~/.xssh # Your .xssh file, that will be sourced on remote system
XSSH_SELF="$0" # Path to this script (change this to absolute path,
# if it doesn't work as expected)
SSH_HOST=
SSH_ARGS=
base64_encode() {
{
command -v openssl >/dev/null 2>&1 && openssl enc -base64 ||
command -v base64 >/dev/null 2>&1 && base64
} | tr -d '\n'
}
base64_decode() {
sed -e "s/.\{64\}/&\n/g" | {
command -v openssl >/dev/null 2>&1 && openssl enc -base64 -d ||
command -v base64 >/dev/null 2>&1 && base64 -d
}
}
if ! [ -t 1 ]; then
# Not in terminal, fallback to ssh
ssh "$@"
exit $?
fi
if ! [ -r "$XSSH_RC" ] && ! [ -n "$_XSSH_RC" ]; then
# Nothing to source, fallback to ssh
ssh "$@"
exit $?
fi
parse_args() {
while [ $# -ne 0 ]; do
case "$1" in
-b | -c | -D | -E | -e | \
-F | -I | -i | -L | -l | \
-m | -O | -o | -p | -Q | \
-R | -S | -W | -w )
SSH_ARGS="$SSH_ARGS $1 $2"; shift ;;
-*)
SSH_ARGS="$SSH_ARGS $1" ;;
*)
if [ -z "$SSH_HOST" ]; then
SSH_HOST="$1"
else
# `command` argument
# no need to use xssh
return 1
fi
esac
shift
done
}
parse_args "$@" || {
# Failed parsing arguments (`command` argument?), fallback to ssh
ssh "$@"; exit $?;
}
# Store .xsshrc contents in `$_XSSH_SOURCE`
if [ -z "$_XSSH_RC" ]; then
export _XSSH_RC=$(cat "$XSSH_RC" | base64_encode)
{ _XSSH_SOURCE=$(base64_encode) ; } <<HEREDOC
_xssh_func() {
$(cat "$XSSH_SELF")
}
HEREDOC
fi
# Bootstrap script
# This will run .xsshrc and create xssh command
{ _XSSH_INIT=$(base64_encode) ; } <<HEREDOC
base64_decode() {
sed -e "s/.\{64\}/&\n/g" | {
command -v openssl >/dev/null 2>&1 && openssl enc -base64 -d ||
command -v base64 >/dev/null 2>&1 && base64 -d
}
}
export _XSSH_RC="$_XSSH_RC"
export _XSSH_SOURCE="$_XSSH_SOURCE"
. <(printf '%s' "\$_XSSH_SOURCE" | base64_decode)
. <(printf '%s' "\$_XSSH_RC" | base64_decode)
alias xssh=_xssh_func
HEREDOC
# Snippet to run bootstap with bash shell
# Used with eval, watch for `;`
{ _XSSH_BASH=$(base64_encode) ; } <<HEREDOC
_xssh_init() {
\$BASH --rcfile <(printf '%s' "$_XSSH_INIT" | base64_decode) -i;
};
HEREDOC
# This script will try to run bash specific code
# If the system is not infected by bash, run login shell instead
{ _XSSH_SAFE_CMD=$(cat) ; } <<HEREDOC
base64_decode() {
sed -e "s/.\{64\}/&\n/g" | {
command -v openssl >/dev/null 2>&1 && openssl enc -base64 -d ||
command -v base64 >/dev/null 2>&1 && base64 -d
}
};
case "\$SHELL" in
*/bash*)
eval \$(printf '%s' "$_XSSH_BASH" | base64_decode)
;;
*)
test -x "\$SHELL" && exec "\$SHELL" -il || exec /bin/sh -il
;;
esac;
_xssh_init;
HEREDOC
# Let the journey begin
ssh -t $SSH_ARGS "$SSH_HOST" "$_XSSH_SAFE_CMD"
Example ~/.xssh
rc file:
alias l='ls -hAlt --color=auto --group-directories-first '
alias ssh='xssh '
# I use `vis` editor, it is somewhat simular to `vim`
alias vis=vim
# Up and Down will complete history in bash
bind '"\e[A": history-search-backward'
bind '"\e[B": history-search-forward'
. ~/.bashrc
PS1="\u@\[\033[1;30m\]\h\[\033[0m\] \W "
# I use st terminal, but probably there is no `st.info` on remote host
TERM=xterm-256color
There is simular project sshrc with different goals. In particular xssh
tries to be "recursive" and won't leave or create any files (even in /tmp
) on remote hosts.
if ! [ -r "$XSSH_RC" ] && ! [ -n "_$XSSH_RC" ]; then
erroneous? The-n "_$..."
has a literal_
. \$\endgroup\$