Pass along variables via ssh - bash

I'm trying to pass along variables via ssh, and wrote two short testscripts (bash)
This one is to the execute the script on the other side (and it works, at least partially)
I start it with executing: 'mms test alpha one'
#!/bin/bash
sshpass -p (password) ssh hellfire#192.168.0.11 'bash /scripts/mms2 "$#"'
The second script that are executed is:
#!/bin/bash
echo "$#" >/scripts/test1.txt
This script is only for testing if the parameters are are transfered.
So far it creates the text file, but it's empty, so I have no idea if there's wrong with both or only one of the script.
Basically I want to pass a set of variables to the script on the server, these variables can contain spaces.
Anybody have any tips?

I found out by #Gordon Davidsson comment that the $(printf "%q " "$#") could be used to send it as a string, so the remote server didn't interpret the variables as different commands.
My new and working script is:
#!/bin/bash
sshpass -p (password) ssh hellfire#192.168.0.11 "bash /scripts/mms2 $(printf "%q " "$#")"

Related

Navigating in ssh server through a local bash script [duplicate]

I already have an ssh agent set up, and I can run commands on an external server in Bash script doing stuff like:
ssh blah_server "ls; pwd;"
Now, what I'd really like to do is run a lot of long commands on an external server. Enclosing all of these in between quotation marks would be quite ugly, and I'd really rather avoid ssh'ing multiple times just to avoid this.
So, is there a way I can do this in one go enclosed in parentheses or something? I'm looking for something along the lines of:
ssh blah_server (
ls some_folder;
./someaction.sh;
pwd;
)
Basically, I'll be happy with any solution as long as it's clean.
Edit
To clarify, I'm talking about this being part of a larger bash script. Other people might need to deal with the script down the line, so I'd like to keep it clean. I don't want to have a bash script with one line that looks like:
ssh blah_server "ls some_folder; ./someaction.sh 'some params'; pwd; ./some_other_action 'other params';"
because it is extremely ugly and difficult to read.
How about a Bash Here Document:
ssh otherhost << EOF
ls some_folder;
./someaction.sh 'some params'
pwd
./some_other_action 'other params'
EOF
To avoid the problems mentioned by #Globalz in the comments, you may be able to (depending what you're doing on the remote site) get away with replacing the first line with
ssh otherhost /bin/bash << EOF
Note that you can do variable substitution in the Here document, but you may have to deal with quoting issues. For instance, if you quote the "limit string" (ie. EOF in the above), then you can't do variable substitutions. But without quoting the limit string, variables are substituted. For example, if you have defined $NAME above in your shell script, you could do
ssh otherhost /bin/bash << EOF
touch "/tmp/${NAME}"
EOF
and it would create a file on the destination otherhost with the name of whatever you'd assigned to $NAME. Other rules about shell script quoting also apply, but are too complicated to go into here.
Edit your script locally, then pipe it into ssh, e.g.
cat commands-to-execute-remotely.sh | ssh blah_server
where commands-to-execute-remotely.sh looks like your list above:
ls some_folder
./someaction.sh
pwd;
To match your sample code, you can wrap your commands inside single or double qoutes. For example
ssh blah_server "
ls
pwd
"
I see two ways:
First you make a control socket like this:
ssh -oControlMaster=yes -oControlPath=~/.ssh/ssh-%r-%h-%p <yourip>
and run your commands
ssh -oControlMaster=no -oControlPath=~/.ssh/ssh-%r-%h-%p <yourip> -t <yourcommand>
This way you can write an ssh command without actually reconnecting to the server.
The second would be to dynamically generate the script, scping it and running.
This can also be done as follows.
Put your commands in a script, let's name it commands-inc.sh
#!/bin/bash
ls some_folder
./someaction.sh
pwd
Save the file
Now run it on the remote server.
ssh user#remote 'bash -s' < /path/to/commands-inc.sh
Never failed for me.
Put all the commands on to a script and it can be run like
ssh <remote-user>#<remote-host> "bash -s" <./remote-commands.sh
Not sure if the cleanest for long commands but certainly the easiest:
ssh user#host "cmd1; cmd2; cmd3"
This works well for creating scripts, as you do not have to include other files:
#!/bin/bash
ssh <my_user>#<my_host> "bash -s" << EOF
# here you just type all your commmands, as you can see, i.e.
touch /tmp/test1;
touch /tmp/test2;
touch /tmp/test3;
EOF
# you can use '$(which bash) -s' instead of my "bash -s" as well
# but bash is usually being found in a standard location
# so for easier memorizing it i leave that out
# since i dont fat-finger my $PATH that bad so it cant even find /bin/bash ..
SSH and Run Multiple Commands in Bash.
Separate commands with semicolons within a string, passed to echo, all piped into the ssh command. For example:
echo "df -k;uname -a" | ssh 192.168.79.134
Pseudo-terminal will not be allocated because stdin is not a terminal.
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda2 18274628 2546476 14799848 15% /
tmpfs 183620 72 183548 1% /dev/shm
/dev/sda1 297485 39074 243051 14% /boot
Linux newserv 2.6.32-431.el6.x86_64 #1 SMP Sun Nov 10 22:19:54 EST 2013 x86_64 x86_64 x86_64 GNU/Linux
The posted answers using multiline strings and multiple bash scripts did not work for me.
Long multiline strings are hard to maintain.
Separate bash scripts do not maintain local variables.
Here is a functional way to ssh and run multiple commands while keeping local context.
LOCAL_VARIABLE=test
run_remote() {
echo "$LOCAL_VARIABLE"
ls some_folder;
./someaction.sh 'some params'
./some_other_action 'other params'
}
ssh otherhost "$(set); run_remote"
For anyone stumbling over here like me - I had success with escaping the semicolon and the newline:
First step: the semicolon. This way, we do not break the ssh command:
ssh <host> echo test\;ls
^ backslash!
Listed the remote hosts /home directory (logged in as root), whereas
ssh <host> echo test;ls
^ NO backslash
listed the current working directory.
Next step: breaking up the line:
v another backslash!
ssh <host> echo test\;\
ls
This again listed the remote working directory - improved formatting:
ssh <host>\
echo test\;\
ls
If really nicer than here document or quotes around broken lines - well, not me to decide...
(Using bash, Ubuntu 14.04 LTS.)
The easiest way to configure your system to use single ssh sessions by default with multiplexing.
This can be done by creating a folder for the sockets:
mkdir ~/.ssh/controlmasters
And then adding the following to your .ssh configuration:
Host *
ControlMaster auto
ControlPath ~/.ssh/controlmasters/%r#%h:%p.socket
ControlMaster auto
ControlPersist 10m
Now, you do not need to modify any of your code. This allows multiple calls to ssh and scp without creating multiple sessions, which is useful when there needs to be more interaction between your local and remote machines.
Thanks to #terminus's answer, http://www.cyberciti.biz/faq/linux-unix-osx-bsd-ssh-multiplexing-to-speed-up-ssh-connections/ and https://en.wikibooks.org/wiki/OpenSSH/Cookbook/Multiplexing.
What is the cleanest way to ssh and run multiple commands in Bash?
I recommend using this escaping function. The function takes one argument - a function to escape. Then sshqfunc outputs declare -f of the function and then outputs a string that will call the function with "$#" arguments properly quoted. Then the whole is "%q" quoted and bash -c is added. In case the remote does not have bash, you could change bash to sh.
sshqfunc() { echo "bash -c $(printf "%q" "$(declare -f "$#"); $1 \"\$#\"")"; };
Then define a function with the work you want to do on the remote. The function is defined normally, so it will be properly "clean". You can test such function locally. After defining, properly escaped function is passed to the remote.
work() {
ls
pwd
echo "Some other command"
}
ssh host#something "$(sshqfunc work)"
Passing You can also pass arguments, and they will be passed to your function as positional arguments. The right next argument after the function will be assigned to $0 - usually a placeholder like -- or _ is used to separate arguments from call.
work() {
file=$1
num=$2
ls "$file"
echo "num is $num"
}
ssh host#something "$(sshqfunc work)" -- /this/file 5
But note that arguments should also be properly quoted if there are any magic characters:
ssh host#something "$(sshqfunc work)" -- "$(printf "%q" "$var1" "$var2")"
For simple commands you can use:
ssh <ssh_args> command1 '&&' command2
or
ssh <ssh_args> command1 \&\& command2

pass variables to shell script over ssh

How do I make $1 and $2 variables to the remote shell through ssh. Below is the sample,
#!/bin/bash
user_name="${1}"
shift
user_password="${1}"
shift
tenant_name="${1}"
realscript="/IDM_ARTIFACTS/reset.sh"
ssh -qT oracle#slc05pzz.us.oracle.com bash -c "'echo $user_name'" < "$realscript"
I am able to echo $user_name but not able to access it in $realscript.
Cant call using HERE tags or single quotes'' as the script doesn't have straight forward commands.
What other options do I have? Please help
I do not have your script, so I put a test one on my remote host:
$ realscript=/home/jack/show_params.sh
$ second="second one"
$ ssh TEST cat ${realscript}
#!/bin/bash
nParams=$#
echo There are ${nParams} parameters.
for (( ii=1; ii<=${nParams}; ii++ )); do
echo "$1"
shift
done
$ ssh TEST 'bash '${realscript}' "first one" '\'${second}\'
There are 2 parameters.
first one
second one
The quoting gets a bit weird, but you can pass into parameters variables with spaces.

Passing environment variables over ssh for remote expansion

I am trying to pass an environment variable to my shell script, and then echo out what I passed in. In this example $HOME is an environment variable already set.
./my_script.ksh $HOME
#! /usr/bin/ksh
my_var=${1}
echo "You sent: ${my_var}"
I want my output to be You sent: $HOME but instead it is evaluating it and is producing You Sent: /home/blah/usr
The point of this is that I plan on doing an SSH command that I need to pass $my_var too un-evaluated (aka be $HOME) because $HOME across each server is a different value and for the SSH command I want it to use the value from the server it is SSH'ing too. so was hoping to just pass it in so during the SSH it would evaluate correctly
Here is an example of what I want to have happen. Let's assume $HOME is already set on Server1 and Server2 like:
Server1 $HOME: /home/blah/usr
Server2 $HOME: /home/superblah/newusr
From Server1 execute my_script.ksh $HOME
The script is the same as above with one extra line of:
ssh my_user#Server2 "echo Server got: $my_var"
What my output currently is:
You sent: /home/blah/usr
Server got: /home/blah/usr
What I want is just that ssh command to produce:
Server got: /home/superblah/newusr
Thanks.
You can prevent resolving the variable by wrap it in single quotes
./my_script.ksh '$HOME'
I tried it like this:
[host1]$ echo $HOME
/home/user1
[host1]$ ssh user2#host2 echo '$HOME'
/home/user2
[host1]$
envsubst can be used to substitute strings of the form $HOME or $foo with the value of like-named environment variables. Thus:
#!/usr/bin/env ksh
my_var=$1
my_var_subst=$(envsubst <<<"$my_var")
echo "You sent: ${my_var}"
echo "Interpreted as: ${my_var_subst}"
...will emit:
You sent: $HOME
Interpreted as: /home/superblah/newusr
when invoked with ./myscript '$HOME'.
Note that one needs to be careful when passing values over SSH, as SSH implicitly runs sh -c "$*" with the arguments passed -- concatenating them together with whitespace and evaluating that single string as a shell command. Thus, running your command over ssh may look like:
ssh somehost $'./myscript \'$HOME\''
...or...
ssh somehost "./myscript \$HOME"
...or, to make it easier, let the shell do the escaping work for you:
remote_command=$(printf '%q ' ./myscript '$HOME')
ssh somehost "$remote_command"

how to add ssh key to host via bash script

I've been trying to automate the creation of a user and configuration of the ssh access.
So far I created a script that access the host and creates the new user via expect, as follows:
expect -c '
spawn ssh '$user'#'$ip';
expect "assword: ";
send "'$passwd'\r";
expect "prompt\n";
send "adduser '$new_user'\r";
...
send "mkdir /home/'$new_user'/.ssh\r";
expect "prompt\n";
send "exit\r";
'
This works fine, after that I need to add the .pub key file to the authorized keys file in the host, there is where hell started.
I tried:
ssh_key='/home/.../key.pub'
content=$(cat $ssh_key)
expect -c '
spawn ssh '$user'#'$ip' "echo '$content' >> /home/'$new_user'/.ssh/authorized_keys;
expect "password:";
...
'
and got:
missing "
while executing
"spawn ssh root#000.00.00.00 ""
couldn't read file "<ssh .pub key content> ...
I tried also:
cat $ssh_key | ssh $user#$ip "cat >> /home/$new_user/.ssh/authorized_keys"
Without success, I only get the password query blinking, I can't connect the expect with this last method.
I'm going to ignore the larger problems here and focus specifically on your question. (There are larger problems: Don't use expect here -- if you rely on sshpass instead you can simplify this script immensely).
Right now, when you close your single quotes, you aren't starting any other kind of quotes. That means that when you substitute a variable with whitespace, you end the -c argument passed to expect.
Instead of doing this:
'foo'$bar'baz'
do this:
'foo'"$bar"'baz'
...so your script will look more like:
ssh_key='/home/.../key.pub'
content=$(<"$ssh_key")
expect -c '
spawn ssh '"$user"'#'"$ip"' "echo '"$content"' >> /home/'"$new_user"'/.ssh/authorized_keys;
expect "password:";
...
'
In terms of avoiding this altogether, though, consider something more like the following:
#!/bin/bash
# ^^^^- NOT /bin/sh
content=$(<"$ssh_key") # more efficient alternative to $(cat ...)
# generate shell-quoted versions of your variables
# these are safe to substitute into a script
# ...even if the original content contains evil things like $(rm -rf /*)
printf -v content_q '%q' "$content"
printf -v new_user_q '%q' "$new_user"
# use those shell-quoted versions remotely
sshpass -f"$password_file" ssh "$host" bash -s <<EOF
adduser ${new_user_q}
printf '%s\n' ${content_q} >>/home/${new_user_q}/.ssh/authorized_keys
EOF

Unable to pass string parameter (multiline) to remote script using SSH

I’m trying to run a script on a remote machine but I’m facing issue with the parameter that I’m passing to that script. I am passing a variable which is a string containing multiline as input.
The main script is named parent.sh which calls child.sh on remote machine. This child.sh is actually copied to the remote machine and then executed on that machine.
$ cat parent.sh
#!/bin/bash
/usr/bin/scp /home/tom/child.sh harry#remote_server:
printf "Key in parent script: $key\n\n"
/usr/bin/ssh -t harry#remote_server "sudo sh -c '/home/harry/child.sh '$key''"
$ cat child.sh
#!/bin/bash
printf "Key in child: $1"
Input:
key has the following content:
fruit=apple
vegetable=potato
Output:
Key in parent script: fruit=apple
vegetable=potato
Key in child: fruit=apple
Problem is that in the child script's output, i'm only getting the first line but not the second line (vegetable=potato). I tried few combinations of single and double quotes but nothing seems to be working. I would really love to know what blunder am i committing here and how can i rectify it.
This command should work:
/usr/bin/ssh -t harry#remote_server "sudo sh -c '/home/harry/child.sh \"$key\"'"
The key here is that you need the inner command to keep the value of $key quoted when it executes.
Your original command:
"sudo sh -c '/home/harry/child.sh '$key''"
which you probably thought was being read as:
"{sudo} {sh} {-c} {/home/harry/child.sh {$key}}"
is actually read as:
"{sudo} {sh} {-c} {/home/harry/child.sh }$key{}"
which leaves the expansion of $key unquoted and it then gets word-split and the newline becomes just another normal space. You probably have the second "line" available as $2 in the script.

Resources