How can I execute a script from my local machine in a specific (but variable) directory on a remote host? - bash

From a previous question, I have found that it is possible to run a local script on a remote host using:
ssh -T remotehost < localscript.sh
Now, I need to allow others to specify the directory in which the script will be run on the remote host.
I have tried commands such as
ssh -T remotehost "cd /path/to/dir" < localscript.sh
ssh -T remotehost:/path/to/dir < localscript.sh
and I have even tried adding DIR=$1; cd $DIR to the script and using
ssh -T remotehost < localscript.sh "/path/to/dir/"
alas, none of these work. How am I supposed to do this?

echo 'cd /path/to/dir' | cat - localscript.sh | ssh -T remotehost
Note that if you're doing this for anything complex, it is very, very important that you think carefully about how you will handle errors in the remote system. It is very easy to write code that works just fine as long as the stars align. What is much harder - and often very necessary - is to write code that will provide useful debugging messages if stuff breaks for any reason.
Also you may want to look at the venerable tool http://en.wikipedia.org/wiki/Expect. It is often used for scripting things on remote machines. (And yes, error handling is a long term maintenance issue with it.)

Two more ways to change directory on the remote host (variably):
echo '#!/bin/bash
cd "$1" || exit 1
pwd -P
shift
printf "%s\n" "$#" | cat -n
exit
' > localscript.sh
ssh localhost 'bash -s "$#"' <localscript.sh '/tmp' 2 3 4 5
ssh localhost 'source /dev/stdin "$#"' <localscript.sh '/tmp' 2 3 4 5
# make sure it's the bash shell to source & execute the commands
#ssh -T localhost 'bash -c '\''source /dev/stdin "$#"'\''' _ <localscript.sh '/tmp' 2 3 4 5

Related

Execute multiple commands on remote server using bash

I want to execute cd and scp commands on a remote server which have to be logged in with a different sudo user. Below code snippet asks for the password(echos on screen) for my user but hangs there. It doesn't execute cd
#!/bin/bash
server=myserver.com
ssh $server 'sudo -S -u <user> -i; cd dir1/dir2/; scp file1 user#local-sever'
The issue is that you have a semi colon before cd and so sudo has no command to execute. Remove the ; and it should work:
ssh $server 'sudo -S -u <user> -i scp dir1/dir2/file1 user#local-sever'
There are several ways to address this, but most boil down to wrapping up the commands into a set of instructions. Raman's solution is good since it handles the issue by using full paths, but sometimes that isn't an option. Here's another take -
Assuming your command list can afford the quotes, I like here-strings.
ssh -t sa-nextgen-jenkins.eng.rr.com <<< "
echo 'set -x; cd /tmp; whoami; touch foo; ls -l foo; rm -f foo;'|sudo -iSu user
"
If you need the quotes, try a here-doc.
ssh -t sa-nextgen-jenkins.eng.rr.com <<END
echo 'set -x; echo "$RANDOM"; cd /tmp; whoami; touch foo; ls -l foo; rm -f foo;'|sudo -iSu $user
END
You can also write a small script that has arbitrarily complex commands and scp it over, then use a remote ssh call to execute it as the relevant user.

open gnome terminal tabs programmatically and execute commands in sequence

When working remotely, I have a series of tabs that I open in gnome-terminal, and commands that I execute in them. I would like to automate all this setup as a single command.
If these commands could run independently and in parallel, I'd just adapt the answer to this question. In fact, I tried, using the following shell script:
gnome-terminal --working-directory="/home/superelectric" --tab -t "gate" -e 'bash -c "export BASH_POST_RC=\"ssh gate_tunnel\"; exec bash"' --tab -t "mydesktop" -e 'bash -c "export BASH_POST_RC=\"ssh tunneled_mydesktop\"; exec bash"'
Spread out over multiple lines, for readability:
gnome-terminal \
--working-directory="/home/superelectric" \
--tab \
-t "gate" \
-e \
'bash -c "export BASH_POST_RC=\"ssh gate_tunnel\"; exec bash"' \
--tab \
-t "mydesktop" \
-e \
'bash -c "export BASH_POST_RC=\"ssh tunneled_mydesktop\"; exec bash"'
The first part opens a tab, names it 'gate', and executes 'ssh gate_tunnel' within it. This is an ssh alias that opens a tunnel to 'mydesktop' at school, through the school's outward-facing server, 'gate'.
The second part opens another tab, names it 'mydesktop', and executes 'ssh tunneled_mydesktop' within it. This is another ssh alias, which connects to mydesktop through the tunnel.
~/.ssh/config:
Host gate_tunnel
LocalForward 8023 <my_desktop_at_school>:22
HostName <my_school_server>
That's the theory. In practice, the two commands execute in parallel, whereas I need to ensure that the first tab's command (open tunnel) completes before executing the second tab's command (connect through tunnel).
Is there maybe some command I can execute in the second tab, that 'waits' until the ssh tunnel is opened?
Ok, I think i get it. As i mentioned in the comments the first thing that comes to mind for reaching your school desktop from the outside is to ssh into the school gate and from there ssh into your desktop with something like:
$ ssh -t gate.school.edu ssh desktop_name
There's only one tab then, so your problem doesn't exist.
However there's something very cool with your current setup:
From home it's almost as if you had a direct connection to your desktop machine, so you can scp into it directly and forget about gate. With the solution above that's not possible anymore because we end up with an indirect connection: If you want to scp you have to do it from gate and that sucks.
Check out this article on using ssh's ProxyCommand feature:
Transparent Multi-hop SSH
You get the best of both worlds then :)
Hmm... this may not be a perfect solution. Ideally you should use something that monitors the ssh connection. But, you can check the ssh process with ps. And wait for ssh command to come alive.
#!/bin/bash
COUNTER=0
while [ $COUNTER -lt 10 ]; do # try 10 times
if ps aux ¦ grep <my_desktop_at_school> then
# the tunnel connected now execute the second command
'bash -c "export BASH_POST_RC=\"ssh tunneled_mydesktop\"; exec bash"'
else
continue # or you could do something here if you wish
fi
sleep 10 # sleep for 10 seconds and try again
let COUNTER=COUNTER+1
done
You will have to run this script in the second tab.
Hope it helps.

ssh login without welcome banner

I am using ssh from a program which sends commands to ssh and parses answers. However, each time I log in, I get the welcome banner like:
Linux mymachine 3.2.0-4-686-pae #1 SMP Debian 3.2.54-2 i686
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
...
I do not want this banner, because my parser would need to deal with it. Is it possible to login with ssh and not to get this banner at the beginning?
You should be able to silence this banner, and other diagnostic messages, by passing -q to SSH:
ssh -q user#remote_host
If you want to make -q permanent for all your SSH sessions, do:
echo "LogLevel QUIET" >> ~/.ssh/config
What works here seems to depend on the operating system, SSH version, and the server-side configuration of sshd.
For connecting to a stock Ubuntu 18 server ssh -q didn't work for me, and neither did ssh -o LogLevel=error that is suggested elsewhere.
What did work is the comment posted under the question about creating a .hushlogin file in the remote user's home directory:
$ ssh myuser#myhost
Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 4.15.0-55-generic x86_64)
<snip>
Last login: Thu Aug 1 14:04:26 2019 from 1.2.3.4
myuser#myhost$ touch .hushlogin
myuser#myhost$ exit
Then:
$ ssh myuser#myhost echo 'Test'
Test
This will run command1 command2 and command3 on the remote_host.
ssh user#remote_host 'command1; command2; command3'
No banners are displayed.
Try ssh -q to supress the banner message
If you expect more than 1000 lines in the server answer then replace 1000 with a corresponding number or the server answer will be truncated.
# Demo script file creation \
DIVIDER="___"; echo "echo $DIVIDER; echo 100; echo 200; echo 300;" > "./test.sh"; \
# \
# Getting the answer without the banner \
ssh -q login#server.name < "./test.sh" | grep -A1000 -e "^$DIVIDER" | tail -n +2
Success
100
200
300
The same command without
| grep -A1000 -e "^$DIVIDER" | tail -n +2
gives
Welcome to Ubuntu 16.04.3 LTS (GNU/Linux...
[...]
Run 'do-release-upgrade' to upgrade to it.
___
100
200
300
You can replace "___" (three underscores) with any exotic sign(s) or even password (which can't be found in the beginnings of lines of the banner).
To avoid the replacing 1000 with a corresponding number (and possible truncation of big server answers) search something about "how to grep all lines after match" and modify my code.
For running commands remotely:
#!/bin/bash
SCRIPT='
#Your commands
'
sshpass -p<pass> ssh -o 'StrictHostKeyChecking no' -p <port> user#host "$SCRIPT"
I answer my own question with the solution based on Keith Reynolds answer. I am using:
ssh my_host bash
allowing bash interaction without banner and without prompt.

shell script to run multiple scripts from different shells

I want to run 2 different scripts from a single master shell script.
The first one uses the following command "rosh -n -l abcd" (It will log me in to the server with the user abcd and on the same shell I need to run the other script#2 and script#3 ...etc.)
Script#2- From there I need to change user using su - xyz and provide a password (it is fine if I can hardcode this in the file) (Script name is logintoServer)
Script#3- Run some script in the same shell to verify start of stop of server...
I have done the following but failed
I have one script which has rosh -n <servername> -l abcd /bin/sh -c "su - xyz" (I have to run this command in the same shell)
The below are the errors:
I am getting error while executing "standard in must be a tty"
I have tried to create 2 different scripts and run, but the problem is once the first script is run it does not run the 2nd script till I exit the script. (I need to run the 2nd script from the sub-shell created by the 1st script....)
I don't have rosh and I don't have a man page for rosh but a similar problem exists with ssh:
ssh localhost /bin/bash -c 'echo x' # (prints nothing)
ssh localhost "/bin/bash -c 'echo x'" # x
ssh localhost "/bin/bash -c 'tty'" # not a tty
ssh -t localhost "/bin/bash -c 'tty'" # /dev/pts/12\nConnection to localhost closed.
ssh localhost "/bin/bash -c 'su - $USER'" # su: must be run from a terminal
ssh -t localhost "/bin/bash -c 'su - $USER'"
the last asked for a password and then gave me a shell, so that would be 2 of 3 steps.
so one idea is to see if rosh has the -t option, too and the other is to enclose /bin/bash... with quotes, too (will require some escaping for the 3rd level).
What does rosh say with equivalent commands?
UPDATE
latest state:
rosh -n $host -l abcd -t "/bin/sh -c 'su - $user'"
Next I would save one step by saying /bin/su - xyz instead /bin/sh -c 'su - xyz', then you can use single quotes later, e.g.
rosh -n $host -l abcd -t "/bin/su - $user -c 'echo $PATH'"
this should print $PATH as seen by the echo command. Apparently it doesn't contain java. try man su, which java, man which.
su ... -c cmd runs cmd with the shell specified in /etc/passwd, so say </etc/passwd grep $user on the remote machine to find out which shell is used. if it's bash you can change $PATH in .bashrc or so, for other shells I don't know exactly.
Or specify an absolute path when launching java.
regarding password: with ssh I managed to use private key / public key and ssh-agent. For rosh I don't know if that works, too.

running multiple commands through ssh and storing the outputs in different files

i've set up my public and private keys and have automated ssh login. I want to execute two commands say command1 and command2 in one login session and store them in files command1.txt and command2.txt on the local machine.
i'm using this code
ssh -i my_key user#ip 'command1 command2' and the two commands get executed in one login but i have no clue as to how to store them in 2 different files.
I want to do so because i dont want to repeatedly ssh into my remote host.
Unless you can parse the actual outputs of the two commands and distinguish which is which, you can't. You will need two separate ssh sessions:
ssh -i my_key user#ip command1 > command1.txt
ssh -i my_key user#ip command2 > command2.txt
You could also redirect the outputs to files on the remote machine and then copy them to your local machine:
ssh -i my_key user#ip 'command1 > command1.txt; command2 > command2.txt'
scp -i my_key user#ip:'command*.txt' .
NO, you will have to do it separately in separate command (multiple login) as already mentioned by #lanzz. To save the output in local, do like
ssh -i my_key user#ip "command1" > .\file_on_local_host.txt
In case, you want to run multiple command in a single login, then jot all your command in a script and then run that script through SSH, instead running multiple command.
It's possible, but probably more trouble than it's worth. If you can generate a unique string that is guaranteed not to be in the output of command1, you can do:
$ ssh remote 'cmd1; echo unique string; cmd2' |
awk '/^unique string$/ { output="cmd2"; next } { print > output }' output=cmd1
This simply starts printing to the file cmd1, and then changes output to the file cmd2 when it sees the unique string. You'll probably want to handle stderr as well. That's left as an exercise for the reader.
option 1. Tell your boss he's being silly. Unless, of course, he isn't and there is critical reason of needing it all in one session. For some reason such a case escapes my imagination.
option 2. why not tar?
ssh -i my_key user#ip 'command1 > out1; command2 > out2; tar cf - out*' | tar xf -
You can do this. Assuming you can set up authentication from the remote machine back to the local machine, you can use ssh to pipe the output of the commands back. The trick is getting the backslashes right.
ssh remotehost command1 \| ssh localhost cat \\\> command1.txt \; command2 \| ssh localhost cat \\\> command2.txt
Or if you aren't so into backslashes...
ssh remotehost 'command1 | ssh localhost cat \> command1.txt ; command2 | ssh localhost cat \> command2.txt'
join them using && so you can have it like this
ssh -i my_key user#ip "command1 > command1.txt && command2 > command2.txt && command3 > command3.txt"
Hope this helps
I was able to, here's exactly what I did:
ssh root#your_host "netstat -an;hostname;uname -a"
This performs the commands in order and cat'd them onto my screen perfectly.
Make sure you start and finish with the quotation marks, else it'll run the first command remotely then run the remainder of the commands against your local machine.
I have an rsa key pair to my server, so if you want to avoid credential check then obviously you have to make that pair.
I think this is what you need:
At first you need to install sshpass on your machine.
then you can write your own script:
while read pass port user ip; do
sshpass -p$pass ssh -p $port $user#$ip <<ENDSSH1
COMMAND 1 > file1
.
.
.
COMMAND n > file2
ENDSSH1
done <<____HERE
PASS PORT USER IP
. . . .
. . . .
. . . .
PASS PORT USER IP
____HERE
How to run multiple command on remote server using single ssh conection.
[root#nismaster ~]# ssh 192.168.122.169 "uname -a;hostname"
root#192.168.122.169's password:
Linux nisclient2 2.6.18-164.el5 #1 SMP Tue Aug 18 15:51:54 EDT 2009 i686 i686 i386 GNU/Linux
nisclient2
OR
[root#nismaster ~]# ssh 192.168.122.169 "uname -a && hostname"
root#192.168.122.169's password:
Linux nisclient2 2.6.18-164.el5 #1 SMP Tue Aug 18 15:51:54 EDT 2009 i686 i686 i386 GNU/Linux
nisclient2

Resources