Expect script SSH not successful - ssh

I have made two scripts:
This one fetches IP address & Hostnames:
#!/bin/bash
for i in `cat ~/script/hosts.txt`
do HOSTNAME=`echo $i|awk -F: '{print $1}'`
IP=`echo $i|awk -F: '{print $2}'`
TIMESTAMP=`date +%Y-%m-%d`
~/script/expect.sh $HOSTNAME $IP
done
This one does SSH into the devices:
#!/usr/bin/expect
set timeout 20
set HOSTNAME [lindex $argv 0]
set IP [lindex $argv 1]
exp_internal 1
spawn ssh -o StrictHostKeyChecking=no root#$IP
exit
I want to make a script to bakcup multiple device configurations.
Problem is that SSH is failing due to following errors:
$ ./main.sh
spawn ssh -o StrictHostKeyChecking=no root#10.102.82.235
: Name or service not knownname 10.102.82.235
spawn ssh -o StrictHostKeyChecking=no root#10.102.82.239
: Name or service not knownname 10.102.82.239
When I debug, I see the following error
spawn id exp4 sent <ssh: Could not resolve hostname 10.102.82.235\r\r: Name or
service not known\r\r\n>
: Name or service not knownname 10.102.82.235
I think the issue is due to these characters: "\r\r", "\r\r\n"
Is there any way I can filter these out?

Not an answer, but your shell script can use much improvement:
#!/bin/bash
while IFS=: read -r host ip; do
timestamp=$(date +%Y-%m-%d %T)
~/script/expect.sh "$host" "$ip"
done < ~/script/hosts.txt
Notes:
don't use for line in `cat file` to read the lines of a file -- a for loop reads words from a file
use $(...) for command substitution, not `...` -- improved readability, and easy to nest
don't use UPPERCASE_VARIABLES -- those should be reserved for the shell's use.
your (unused) TIMESTAMP variable actually contains a date, no time.
quote your "$variables" unless you can explain why you want them unquoted.

Related

Pass arguments for SQL statement in a shell file from another shell file through ssh command

I am passing command line arguments to a shell file i.e assignRole.sh which contains an SQL command which will use these arguments like below
ssh -o StrictHostKeyChecking=no -T $key < /oracle/oracle_user/makhshif/./assignRole.sh name open_mode >> /oracle/oracle_user/dftest.txt
This gives me error and does not accept arguments of name and open_mode and gives error, but if I execute the statement outside of ssh command like:
/oracle/oracle_user/makhshif/./assignRole.sh name open_mode
This runs fine
What is the problem with ssh command and how should I adjust these parameters so these can be accepted for the shell script assignRole.sh
< /oracle/oracle_user/makhshif/./assignRole.sh
This commands sends a content of that file to stdin. So obviously it can't process variables that you haven't send to remote machine. Just preprocess your script or create a script on remote machine and call it with arguments
Though it's even easier to pass variables like this:
ssh -o StrictHostKeyChecking=no -T $key "var1=$var1 var2=$var2" < /oracle/oracle_user/makhshif/./assignRole.sh name open_mode >> /oracle/oracle_user/dftest.txt
For example my function for executing update scripts on all cluster nodes:
# functions:
ssh_exec(){
local DESCR="$1"; shift
local SCRIPT="$1"; shift
local hosts=("$#")
echo =================================================
echo = $DESCR
echo = Going to execute $SCRIPT...
read -a res -p "Enter 'skip' to skip this step or press Enter to execute: "
if [[ $res = "skip" ]]
then
echo Skipping $SCRIPT...
else
echo Executing $SCRIPT...
for host in "${hosts[#]}"
do
local cur=${!host}
echo Executing $SCRIPT on $host - $cur...
sshpass -p "$rootpass" ssh -o "StrictHostKeyChecking no" root#${cur} \
"ns1=$ns1 ns2=$ns2 search=$search zoo1=$zoo1 zoo2=$zoo2 zoo3=$zoo3 node0=$node0 pass=$pass CURIP=$cur CURHOST=$host bash -s" \
<$SCRIPT >log-$SCRIPT-$cur.log 2>&1
echo Done.
done
echo =================================================
fi
}
Then I use it like this:
read -p "Please check that Solr started successfully and Press [Enter] key to continue..."
#Solr configset and collections:
ssh_exec "Solr configset and collections" script06.sh zoo1 zoo2 zoo3
This command executes script06.sh on 3 servers (zoo1,zoo2,zoo3)
As Sayan said, using < redirects the output of running the assignRole.sh script locally, but you want to execute that script on the remote host, with the arguments.
Pass the whole command as the final argument to ssh, in quotes:
ssh -o StrictHostKeyChecking=no -T $key "/oracle/oracle_user/makhshif/./assignRole.sh name open_mode" >> /oracle/oracle_user/dftest.txt
or split into multiple lines for readability:
ssh -o StrictHostKeyChecking=no -T $key \
"/oracle/oracle_user/makhshif/./assignRole.sh name open_mode" \
>> /oracle/oracle_user/dftest.txt

Running a script when connecting to server with ssh

I use the kitty terminal emulator, so when I connect to a new server, I (usually) need to ad the terminfo (at least, this way it seems to work). To do this I wrote a script. While I was at it, I added a bit of code to add a public key if the user wants it to.
Not really relevant for the question, but here is the code:
#!/bin/bash
host=$1
ip=$(echo $host | cut -d# -f2 | cut -d: -f1)
# Check if it is a unknown host
if [[ -z $(ssh-keygen -F $ip) ]]; then
# Check if there are any ssh-keys
if [ $(ls $HOME/.ssh/*.pub > /dev/null | wc -l) -ne 0 ]; then
keys=$(echo $( (cd $HOME/.ssh/ && ls *.pub) | sed "s/.pub//g" ))
ssh -q -o PubkeyAuthentication=yes -o PasswordAuthentication=no $host "ls > /dev/null 2>&1"
# Check if the server has one of the public keys
if [ $? -ne 0 ]; then
echo "Do you want to add a SSh key to the server?"
while true; do
read -p " Choose [$keys] or leave empty to skip: " key
if [[ -z $key ]]; then
break
elif [[ -e $HOME/.ssh/$key ]]; then
# Give the server a public key
ssh $host "mkdir -p ~/.ssh && chmod 700 ~/.ssh && echo \"$(cat $HOME/.ssh/$key.pub)\" >> ~/.ssh/authorized_keys"
break
else
echo "No key with the name \"$key\" found."
fi
done
fi
fi
# Copy terminfo to server
ssh -t $host "echo \"$(infocmp -x)\" > \"\$TERM.info\" && tic -x \"\$TERM.info\" && rm \$TERM.info"
fi
It is not the best code, but it seems to work. Tips are ofcourse welcome.
The problem is that I need to run this script every time I connect te a new remote server (or I need to keep track of which server is new, but that is even worse). Is there a way to run this script every time I connect to a server (the script checks if the ip is a known host).
Or is there an other way to do this? Adding the public keys is nice to have, but not very important.
I hope somone can help,
Thanks!
There is a trick to identify that you are using ssh to login on the target machine:
pgrep -af "sshd.*"$USER |wc -l
The above command will count the user's processes using sshd
You can add the above command in the target machine, to test if you are connected via ssh. Add the above command to your .profile or .bash_profile script in the target machine.
So that only if you login via ssh your script will run initiation script on the target machine when you login/connect.
Sample .bash_profile on target machine
#!/bin/bash
# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
# User specific environment and startup programs
if [[ $(pgrep -af "sshd.*"$USER |wc -l) -gt 0 ]]; then
your_init_script
fi

GNU Parallel -q option causing BCP "unknown option" errors (different string quotes on local vs remote hosts)

Seeing very strange behavior where when when using gnu parallel to distribute export jobs using bcp from mssql-tools. It appears that when using the -q option for parallel, strings are interpreted differently on local host than on remote hosts.
Running only as a loop through files on local host, the bcp processes throws no errors
However, distributing the file exports with parallel, the bcp processes executing on the local host throw
/opt/mssql-tools/bin/bcp: unknown option
errors, while those executing on remote hosts (via a --sshloginfile param) finish successfully. The basic code being run looks like...
# setting some vars to pass
TO_SERVER_ODBCDSN="-D -S MyMSSQLServer"
TO_SERVER_IP="-S 172.18.54.22"
DB="$dest_db" #TODO: enforce being more careful with this value
TABLE="$tablename" # MUST exist beforehand, case matters
USER=$(tail -n+1 $source_home/mssql-creds.txt | head -1)
PASSWORD=$(tail -n+2 $source_home/mssql-creds.txt | head -1)
DATAFILES="/some/path/to/files/"
TARGET_GLOB="*.tsv"
RECOMMEDED_IMPORT_MODE='-c' # makes a HUGE difference, see https://stackoverflow.com/a/16310219/8236733
DELIMITER="\\\t" # (currently not used) DO NOT use format like "'\t'", nested quotes seem to cause hard-to-catch error, want "\t" literal
....
bcpexport() {
filename=$1
TO_SERVER_ODBCDSN=$2
DB=$3
TABLE=$4 # MUST exist beforehand, case matters
USER=$5
PASSWORD=$6
RECOMMEDED_IMPORT_MODE=$7 # makes a HUGE difference, see https://stackoverflow.com/a/16310219/8236733
DELIMITER=$8 # not currently used
WORKDIR=$9
LOGDIR=${10}
....
/opt/mssql-tools/bin/bcp "$TABLE" in "$localfile" \
$TO_SERVER_ODBCDSN \
-U $USER -P $PASSWORD \
-d $DB \
$RECOMMEDED_IMPORT_MODE
-t "\t" \
-e ${localfile}.bcperror.log
}
export -f bcpexport
parallelization_pernode=5
parallel -q -j $parallelization_pernode \
--sshloginfile $source_home/parallel-nodes.txt \
--env bcpexport \
bcpexport {} "$TO_SERVER_ODBCDSN" $DB $TABLE $USER $PASSWORD $RECOMMEDED_IMPORT_MODE $DELIMITER $workingdir $logdir \
::: $DATAFILES/$TARGET_GLOB #from hdfs nfs gateway
Looking at the bash interpretation of the processes (by running ps -aux | grep bcp on the hosts that parallelis given in the --sshloginfile) for the remote hosts we see...
/bin/bash -c bcpexport() { ... /opt/mssql-tools/bin/bcp "$TABLE" in "$localfile" $TO_SERVER_ODBCDSN -U $USER -P $PASSWORD -d $DB $RECOMMEDED_IMPORT_MODE; -t "\t" -e ${localfile}.bcperror.log; ...
for the local host, the bash interpretation is...
/bin/bash -c bcpexport() { ... /opt/mssql-tools/bin/bcp "$TABLE" in "$localfile" $TO_SERVER_ODBCDSN -U $USER -P $PASSWORD -d $DB $RECOMMEDED_IMPORT_MODE; -t "\t" -e ${localfile}.bcperror.log; ...
that is, they look the same.
My current thought is that the "\t" in the bcp command is being interpreted in a problematic way. Debugging parallel without vs with the -q option we see...
$ parallel -j 5 --sshloginfile ./parallel-nodes.txt echo "Number {}: Running on \`hostname\`: \t" ::: 1 2 3 4 5
Number 4: Running on HW04.ucera.local: t
Number 1: Running on HW04.ucera.local: t
Number 2: Running on HW03.ucera.local: t
Number 5: Running on HW03.ucera.local: t
Number 3: Running on HW02.ucera.local: t
$ parallel -q -j 5 --sshloginfile ./parallel-nodes.txt echo "Number {}: Running on \`hostname\`: \t" ::: 1 2 3 4 5
Number 1: Running on `hostname`:
Number 4: Running on `hostname`:
Number 3: Running on `hostname`: \t
Number 2: Running on `hostname`: \t
Number 5: Running on `hostname`: \t
The bcp command needs the "\t" literal not the "t" literal (and I suspect several other similar string corruptions (also I do believe that \t is the default for bcp anyway, but this is just an example and want to keep \t for code clarity)), but not sure how to get this for both local and remote nodes or even why this behavior differs by remote vs local.
Basically, need the the strings to be exactly the same for both local and remote hosts even if strings have spaces or escape characters in them (note, I think this used to not be the case (have older script on other machines that don't have this problem))
Not sure if this is counts more as a parallel problem or a bcp problem (currently thinking something is going wrong with the -q option in parallel, but not sure). Anyone have any debugging suggestions or fixes? Ideas of what could be happening?
Firstly, the reason why hostname is not expanded is due to -q. It quotes the ` so that it does not expand.
Secondly, I think what you see is the different behaviours in built-in echo and /bin/echo. Built-in echo depends on the shell. Here I compare echo \\\\t in different shells:
$ parallel --onall --tag -S sh#lo,bash#lo,csh#lo,tcsh#lo,ksh#lo,zsh#lo echo \\\\t ::: a
bash#lo \t a
tcsh#lo a
sh#lo a
ksh#lo \t a
zsh#lo a
csh#lo \t a
That does not, however, get you closer to a solution. If I were you I would use env_parallel to copy the environment variables. And if the login shell on the remote systems are not the same as your shell, then set PARALLEL_SHELL to force using that shell.
So:
#!/bin/bash
env_parallel --session
# setting some vars to pass
TO_SERVER_ODBCDSN="-D -S MyMSSQLServer"
:
:
PARALLEL_SHELL=bash env_parallel -q -j $parallelization_pernode ...
(no need to use neither --env nor 'export -f' when using 'env_parallel --session')
# Cleanup (not needed if this is the last line in the script)
env_parallel --end-session

How to deal with "Pseudo-terminal will not be allocated because stdin is not a terminal."

ssh -t remotehost vim /tmp/x.txt
I know that I can run a command like the above.
But I would like to be able to run any local bash code in a remote machine. For this reason, I'd like to call the remote 'bash -s' so that can process any local bash code.
ssh -t remotehost 'bash -s' <<< vim /tmp/x.txt
However, the above example shows "Pseudo-terminal will not be allocated because stdin is not a terminal." Is there any way to let ssh take local bash code via stdin and run it via the remote 'bash -s'? Thanks.
ssh -t remotehost 'bash -s' <<< vim /tmp/x.txt
You're getting the "Pseudo-terminal will not be allocated..." message because you're running ssh with a single -t option, when the standard input to the ssh process isn't a TTY. ssh prints that message specifically in this case. The documentation for -t says:
-t
Force pseudo-terminal allocation. This can be used to execute arbitrary screen-based programs on a remote machine, which can be very useful, e.g. when implementing menu services. Multiple -t options force tty allocation, even if ssh has no local tty.
The -t command-line option is related to the ssh configuration option RequestTTY:
RequestTTY
Specifies whether to request a pseudo-tty for the session. The argument may be one of: no (never request a TTY), yes (always request a TTY when standard input is a TTY), force (always request a TTY) or auto (request a TTY when opening a login session). This option mirrors the -t and -T flags for ssh(1).
A single -t is equivalent to "RequestTTY yes", while two of them is equivalent to "RequestTTY force".
If you want your remote command(s) to run with a TTY, then specify -t twice:
ssh -tt remotehost 'bash -s' <<< vim /tmp/x.txt
or
ssh -t -t remotehost 'bash -s' <<< vim /tmp/x.txt
ssh will allocate a TTY for the remote system and it won't print that message.
If the command(s) being run on the remote system don't require a TTY, you can leave the -t option out:
ssh remotehost 'bash -s' <<< vim /tmp/x.txt
I believe the following might suit your purposes:
vim /tmp/x.txt ; ssh remotehost 'bash -s' < /tmp/x.txt
The first expression (vim ...) allows you to specify the commands you want to execute remotely as a local file called /tmp/x.txt; the second expression (ssh ...) calls the remote bash interpreter, and sends the contents of your local file to it for execution. Note that you do not need the -t option for ssh in this case (which gave rise to the pseudo-terminal warning), and that you do not need to use a here string (<<<) but can use the normal file input operator (<).
This solution seems to work for, e.g., the following file contents:
echo These commands are being executed on $HOSTNAME
echo This is a second command

How to automatically start tmux on SSH session?

I have ten or so servers that I connect to with SSH on a regular basis. Each has an entry in my local computer's ~/.ssh/config file.
To avoid losing control of my running process when my Internet connection inevitably drops, I always work inside a tmux session. I would like a way to have tmux automatically connect every time an SSH connection is started, so I don't have to always type tmux attach || tmux new after I SSH in.
Unfortunately this isn't turning out to be as simple as I originally hoped.
I don't want to add any commands to the ~/.bashrc on the servers because I only want it for SSH sessions, not local sessions.
Adding tmux attach || tmux new to the ~/.ssh/rc on the servers simply results in the error not a terminal being thrown after connection, even when the RequestTTY force option is added to the line for that server in my local SSH config file.
Server-side configuration:
To automatically start tmux on your remote server when ordinarily logging in via SSH (and only SSH), edit the ~/.bashrc of your user or root (or both) on the remote server accordingly:
if [[ $- =~ i ]] && [[ -z "$TMUX" ]] && [[ -n "$SSH_TTY" ]]; then
tmux attach-session -t ssh_tmux || tmux new-session -s ssh_tmux
fi
This command creates a tmux session called ssh_tmux if none exists, or reattaches to a already existing session with that name. In case your connection dropped or when you forgot a session weeks ago, every SSH login automatically brings you back to the tmux-ssh session you left behind.
Connect from your client:
Nothing special, just ssh user#hostname.
Don't do this on the server-side!
That is potentially dangerous because you can end up being locked-out of the remote machine. And no shell hacks / aliases / etc. are required, either.
Instead...
... make use of (your client's) ~/.ssh/config like so:
tmux 3.1 or newer¹ on the remote machine
Into your local ~/.ssh/config, put²:
Host myhost
Hostname host
User user
RequestTTY yes
RemoteCommand tmux new -A -s foobar
As pointed out by #thiagowfx, this has the side effect of making it impossible to use, e.g. ssh myhost ls /tmp and should therefore not be used with Host * ... what I like to do is to have a Host myhost section with RemoteCommand tmux ... and then in addition to that I'll have a Host MYHOST section without it.
Instead of RequestTTY yes you could call ssh with the -t switch; thank you, #kyb.
Off-topic, but if you're dealing with non-ASCII characters, I'd recommend to change that into tmux -u … for explicitly enabling Unicode support even on machines that don't have the proper environment variables set.
tmux 3.0a or older on the remote machine
Almost the same as above, but change the last line to³:
RemoteCommand tmux at -t foobar || tmux new -s foobar
¹ repology.org has a list of distros and their tmux versions
² new is short for new-session.
³ at is short for attach-session.
Only if, for some reason, you really, really can't do it client-side:
Using the remote's authorized_keys file
If you would rather not have an ~/.ssh/config file for whatever reason, or want the remote machine to force the connecting machine to connect to / open the session, add this to your remote ~/.ssh/authorized_keys:
command="tmux at -t foobar || tmux new -s foobar" pubkey user#client
This will, of course, work from all clients having the corresponding private key installed, which some might consider an upside –– but: should anything go wrong, it might not be possible to connect anymore without (semi-)physical access to the machine!
One caveat!
As #thiagowfx notes in the comments, this should not be put underneath Host * as it breaks certain things, such as git push. What I personally do is to add a second entry in all-uppercase letters for where I want to automatically be connected to tmux.
Alright, I found a mostly satisfactory solution. In my local ~/.bashrc, I wrote a function:
function ssh () {/usr/bin/ssh -t "$#" "tmux attach || tmux new";}
which basically overwrites the ssh terminal function to call the built-in ssh program with the given arguments, followed by "tmux attach || tmux new".
(The $# denotes all arguments provided on the command line, so ssh -p 123 user#hostname will be expanded to ssh -t -p 123 user#hostname "tmux attach || tmux new")
(The -t argument is equivalent to RequestTTY Force and is necessary for the tmux command.)
Connect:
ssh user#host -t "tmux new-session -s user || tmux attach-session -t user"
During session:
Use Ctrl+d to finish session (tmux window closes) or Ctrl+b d to temporary detach from session and connect to it again later.
Remember! If your server restarted session lost!
When you are inside tmux anytime you can use Ctrl+b s to see sessions list and switch current to another.
Fix your .bashrc:
I recommend you to define universal function in your .bashrc:
function tmux-connect {
TERM=xterm-256color ssh -p ${3:-22} $1#$2 -t "tmux new-session -s $1 || tmux attach-session -t $1"
}
It uses 22 port by default. Define your fast-connect aliases too:
alias office-server='tmux-connect $USER 192.168.1.123'
alias cloud-server='tmux-connect root my.remote.vps.server.com 49281'
Login without password:
And if you don't want to type password everytime than generate .ssh keys to login automatically:
ssh-keygen -t rsa
eval "$(ssh-agent -s)" && ssh-add ~/.ssh/id_rsa
Put your public key to the remote host:
ssh-copy-id -p <port> user#hostname
Additional tips:
If you want to use temporary session-id which corresponds with a local bash session use as tmux id:
SID=$USER-$BASHPID
ssh user#host -t "tmux new-session -s $SID || tmux attach-session -t $SID"
I used lines from #kingmeffisto (I'm not allowed to comment that answer) and I added an exit so terminating tmux also terminates the ssh connection. This however broke SFTP sessions so I had to check for $SSH_TTY instead of $SSH_CONNECTION.
EDIT 4/2018: Added test for interactive terminal via [[ $- =~ i ]] to allow tools like Ansible to work.
if [ -z "$TMUX" ] && [ -n "$SSH_TTY" ] && [[ $- =~ i ]]; then
tmux attach-session -t ssh || tmux new-session -s ssh
exit
fi
As described in this blog post you can ssh and then attach to an existing tmux session with a single command:
ssh hostname -t tmux attach -t 0
This is the one that actually creates a great user-experience.
It automatically starts tmux whenever you open the terminal (both physically and ssh).
You can start your work on one device, exit the terminal, and resume on the other one. If it detects someone already attached to the session it will create new session.
Put it on the server, depending on your shell ~/.zshrc or ~/.bashrc.
if [[ -z "$TMUX" ]] ;then
ID="$( tmux ls | grep -vm1 attached | cut -d: -f1 )" # get the id of a deattached session
if [[ -z "$ID" ]] ;then # if not available attach to a new one
tmux new-session
else
tmux attach-session -t "$ID" # if available attach to it
fi
fi
I have the following solution that gives you two SSH hosts to connect to: one with tmux, one without:
# Common rule that 1) copies your tmux.conf 2) runs tmux on the remote host
Host *-tmux
LocalCommand scp %d/.tmux.conf %r#%n:/home/%r/
RemoteCommand tmux new -As %r
RequestTTY yes
PermitLocalCommand yes
# Just connect.
# Notice the asterisk: makes possible to re-use connection parameters
Host example.com*
HostName example.com
User login
# Connect with tmux
Host example.com-tmux
HostKeyAlias dev.dignio.com
You might find this useful - uses ssh in a loop and reconnects to or connects to an existing tmux session so you have a nice easy reliable
way to reconnect after a network outage
#!/bin/bash
#
# reconnect to or spawn a new tmux session on the remote host via ssh.
# If the network connection is lost, ssh will reconnect after a small
# delay.
#
SSH_HOSTNAME=$1
TMUX_NAME=$2
PORT=$3
if [[ "$PORT" != "" ]]
then
PORT="-p $PORT"
fi
if [ "$TMUX_NAME" = "" ]
then
SSH_UNIQUE_ID_FILE="/tmp/.ssh-UNIQUE_ID.$LOGNAME"
if [ -f $SSH_UNIQUE_ID_FILE ]
then
TMUX_NAME=`cat $SSH_UNIQUE_ID_FILE`
TMUX_NAME=`expr $TMUX_NAME + $RANDOM % 100`
else
TMUX_NAME=`expr $RANDOM % 1024`
fi
echo $TMUX_NAME > $SSH_UNIQUE_ID_FILE
TMUX_NAME="id$TMUX_NAME"
fi
echo Connecting to tmux $TMUX_NAME on hostname $SSH_HOSTNAME
SLEEP=0
while true; do
ssh $PORT -o TCPKeepAlive=no -o ServerAliveInterval=15 -Y -X -C -t -o BatchMode=yes $SSH_HOSTNAME "tmux attach-session -t $TMUX_NAME || tmux -2 -u new-session -s $TMUX_NAME"
SLEEP=10
if [ $SLEEP -gt 0 ]
then
echo Reconnecting to session $TMUX_NAME on hostname $SSH_HOSTNAME in $SLEEP seconds
sleep $SLEEP
fi
done
byobu is a nice useful wrapper for tmux/screen. Connects to an existing session if present or creates a new one.
I use it with autossh which gracefully reconnects the ssh session. Highly recommended in case of intermittent connectivity issues.
function ssh-tmux(){
if ! command -v autossh &> /dev/null; then echo "Install autossh"; fi
autossh -M 0 $* -t 'byobu || {echo "Install byobu-tmux on server..."} && bash'
}
I know I'm reviving an old thread but I've done some work on the bashrc solution and I think it has some use:
#attach to the next available tmux session that's not currently occupied
if [[ -z "$TMUX" ]] && [ "SSH_CONNECTION" != "" ];
then
for i in `seq 0 10`; do #max of 10 sessions - don't want an infinite loop until we know this works
SESH=`tmux list-clients -t "$USER-$i-tmux" 2>/dev/null` #send errors to /dev/null - if the session doesn't exist it will throw an error, but we don't care
if [ -z "$SESH" ] #if there's no clients currently connected to this session
then
tmux attach-session -t "$USER-$i-tmux" || tmux new-session -s "$USER-$i-tmux" #attach to it
break #found one and using it, don't keep looping (this will actually run after tmux exits AFAICT)
fi #otherwise, increment session counter and keep going
done
fi
There's a cap at 10 (11) sessions for now - I didn't want to kill my server with an infinite loop in bashrc. It seems to work pretty reliably, other than the error of tmux failing on list-clients if the session doesn't exist.
Thie way allows you to reconnect to an old tmux instance if your ssh session drops. The exec saves a fork of course.
if [ -z "$TMUX" ]; then
pid=$(tmux ls | grep -vm1 "(attached)" | cut -d: -f1)
if [ -z "$pid" ]; then
tmux new -d -s $pid
fi
exec tmux attach -t $pid
fi
Append to bottom of your remote server's ~/.bashrc, (or possibly its /etc/.bashrc.shared (1)):
# ======================== PUT THIS LAST IN .BASHRC ==========================
# --- If we're run by SSH, then auto start `tmux` and possibly re-attach user.
# $- interactive only via current option flags
# -z $TMUX no tmux nesting
# $SSH_TTY SSH must be running, and in a shell
#
if [[ $- == *i* ]] && [[ -z "$TMUX" ]] && [[ -n "$SSH_TTY" ]]; then
tmux attach-session -t "$USER" || tmux new-session -s "$USER" && exit
fi
Many good tips above combined here, e.g. $- and $SSH_TTY are better I think.
And I like adding a few comments to help this old guy remember what's going on without having to look it up.
And finally, I like an exit at the end to cleanly come home when I'm done.
Thanks everyone.
Note I source a shared /etc/.bashrc.shared at the end of both user and root's .bashrc's, for common stuff used in both, like colorized ls, various aliases, functions and path extensions, i.e. I don't want redundant code in my root/.bashrc nor user/.bashrc.
This guys script works great. Just copy the bashrc-tmux file to ~/.bashrc-tmux and source it from ~/.bashrc right after the PS1 check section.