Bash script variable not being passed via ssh - bash

I have a bash script which ssh's to a server, and depending on the status of a variable performs a task:
#!/bin/bash
foo=$1
ssh user#host.com '
echo In host
if [ "$foo" == "yes" ]; then
echo "Foo!"
fi
'
When I run sh script.sh yes, although the ssh command works, the conditional evaluates to false. I can see this if I echo $foo - it prints an empty line. How can I access the value of foo within the ssh command?

Variables aren't transferred to a remote machine. You can expand the variable in the code sent through ssh, but you have to be extremely careful because it opens the door to uncontrolled code execution:
#!/bin/bash
foo=$1
ssh user#host.com '
echo In host
if [ "'"$foo"'" == "yes" ]; then
echo "Foo!"
fi
'
Now imagine (don't try) what happens if foo='$(rm -rf /)'.

Related

ssh bash -c exit status does not propagate [duplicate]

This question already has an answer here:
How to have simple and double quotes in a scripted ssh command
(1 answer)
Closed 4 years ago.
According to man ssh and this previous answer, ssh should propagate the exit status of whatever process it ran on the remote server. I seem to have found a mystifying exception!
$ ssh myserver exit 34 ; echo $?
34
Good...
$ ssh myserver 'exit 34' ; echo $?
34
Good...
$ ssh myserver bash -c 'exit 34' ; echo $?
0
What?!?
$ ssh myserver
ubuntu#myserver $ bash -c 'exit 34' ; echo $?
34
So the problem does not appear to be either ssh or bash -c in isolation, but their combination does not behave as I would expect.
I'm designing a script to be run on a remote machine that needs to take an argument list that's computed on the client side. For the sake of argument, let's say it fails if any of the arguments is not a file on the remote server:
ssh myserver bash -c '
for arg ; do
if [[ ! -f "$arg" ]] ; then
exit 1
fi
done
' arg1 arg2 ...
How can I run something like this and effectively inspect its return status? The test above seems to suggest I cannot.
The problem is that the quoting is being lost. ssh simply concatenates the arguments, it doesn't requote them, so the command you're actually executing on the server is:
bash -c exit 34
The -c option only takes one argument, not all the remaining arguments, so it's just executing exit; 34 is being ignored.
You can see a similar effect if you do:
ssh myserver bash -c 'echo foo'
It will just echo a blank line, not foo.
You can fix it by giving a single argument to ssh:
ssh myserver "bash -c 'exit 34'"
or by doubling the quotes:
ssh myserver bash -c "'exit 34'"
Insofar as your question is how to run a command remotely while passing it on ssh's command line without it getting in a mangle that triggers the bug in question, printf '%q ' can be used to ask the shell to perform quoting on your behalf, to build a string which can then be passed to ssh:
printf -v cmd_str '%q ' bash -c '
for arg ; do
if [[ ! -f "$arg" ]] ; then
exit 1
fi
done
' arg1 arg2 ...
ssh "$host" "$cmd_str"
However, this is only guaranteed to work correctly if the default shell for the remote user is also bash (or, if you used ksh's printf %q locally, if the remote shell is ksh). It's much safer to pass your script text out-of-band, as on stdin:
printf -v arg_str '%q ' arg1 arg2 ...
ssh "$host" "bash -s $arg_str" <<'EOF'
for arg; do
if [[ ! -f "$arg" ]]; then
exit 1
fi
done
EOF
...wherein we still depend on printf %q to generate correct output, but only for the arguments, not for the script itself.
Try wrapping in quotes:
╰─➤ ssh server "bash -c 'exit 34' "; echo $?
34

I am unable to echo the ssh which i looge in using bash script

#!/bin/bash
read -p 'please enter the ssh server name: ' ssh_name
ssh ${ssh_name} 'bash' <<'EOF'
echo ${ssh_name}
exit
EOF
Because you put single quotes around your heredoc limit string it does not expand the variables in place, so it tries to do the variable expansion on the server side, where that variable is not set.
If you want to have the variables expanded locally, just do not use the single quotes around EOF:
read -p 'please enter the ssh server name: ' ssh_name
ssh ${ssh_name} 'bash' <<EOF
echo ${ssh_name}
exit
EOF
This will expand ssh_name on the local side then echo will see the already expanded string on the remote end and echo it.
If you want to pass that variable to the other side, you could make it part of the command you're executing:
read -p 'please enter the ssh server name: ' ssh_name
ssh ${ssh_name} remote_name="$ssh_name" 'bash' <<'EOF'
echo ${remote_name}
exit
EOF
(though you do not have to change the name of the variable, I just did that to highlight that it's available on the remote side).
here is some good reading about heredocs and herestrings.
Becuase $ssh_name is set in the shell you are calling ssh from, and you are trying to echo $ssh_name from within the ssh session itself, where it is not set.
If you move the echo statement to after the EOF or before the ssh statement, then you will find that it works just fine.

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.

ssh bash receive variable from a remote file

I need to read the variable from a remote file over SSH and compare it. But I get a variable in the wrong format. how to do it correctly?
#!/bin/bash
pass='dpassspass'
user='root#10.10.19.18'
IP="10.2.1.41"
path=/sys/variable/serv
#not work## No such file or directory# write=$(sshpass -p $ovhpass ssh -t $user echo "$IP" > $path)
sshpass -p $pass ssh -t $user << EOF
echo "$IP" > $path
EOF
my_var=$(sshpass -p $pass ssh -t $user "cd /sys_ovh; ./serv.bash")
echo mystart-"$my_var"-myend
read=$(sshpass -p $pass ssh -t $user cat $path)
echo start-"$read"-end
echo start-"$IP"-end
if [ "$read" == "$IP" ]; then
echo "run"
fi
output:
Connection to 10.10.19.18 closed.
-myendt-10.2.1.41
Connection to 10.10.19.18 closed.
-endt-10.2.1.41
start-10.2.1.41-end
Where I make a mistake? How to take data from the SSH?
The vars my_var and read are filled with a string ending with '\r', telling echo to go back to the first column. I think this is a problem with your local script. You can correct that with
tr -d "\r" < myfile > myfile2
Your fundamental problem comes from using unquoted here documents for the commands. You should properly understand in which order the shell interprets these contructs.
ssh remote cmd >file
executes cmd remotely, but first redirects the output from the ssh command to the local file.
ssh remote "cmd >’$file'"
The quotes cause the redirection to be part of the remote command line. The variable file is interpreted first, by the local shell, though.
ssh remote 'cmd >"$file"`
The single quotes prevent the local shell from modifying the command before sending it. Thus, he variable interpolation and the redirection are both handled by the remote shell, in this order.
So your commented-out "not work" command could easily be fixed with proper quoting. However, it will be much more elegant and efficient to use a single remote session, and execute all the commands in one go. Mixing the local variable IP with remote variables calls for some rather elaborate escaping, though. A major simplification would be to pass the value on standard input, so that the entire remote script can be single quoted.
#!/bin/bash
pass='dpassspass'
user='root#10.10.19.18'
IP="10.2.1.41"
result=$(echo "$IP" |
sshpass -p "$pass" ssh -t "$user" '
path=/sys/variable/serv
cat > "$path"
cd /sys_ovh
./serv.bash
cat "$path"')
echo mystart-"${result%$'\n'*}"-myend
echo start-"${result#*$'\n'}"-end
echo start-"$IP"-end
if [ "${result#*$'\n'}" == "$IP" ]; then
echo "run"
fi
The output from the remote shell is two lines; we pick it apart by using the shell's prefix and suffix substitution operators.

Pseudo-terminal will not be allocated because stdin is not a terminal ssh bash

okay heres part of my code when I ssh to my servers from my server.txt list.
while read server <&3; do #read server names into the while loop
serverName=$(uname -n)
if [[ ! $server =~ [^[:space:]] ]] ; then #empty line exception
continue
fi
echo server on list = "$server"
echo server signed on = "$serverName"
if [ $serverName == $server ] ; then #makes sure a server doesnt try to ssh to itself
continue
fi
echo "Connecting to - $server"
ssh "$server" #SSH login
echo Connected to "$serverName"
exec < filelist.txt
while read updatedfile oldfile; do
# echo updatedfile = $updatedfile #use for troubleshooting
# echo oldfile = $oldfile #use for troubleshooting
if [[ ! $updatedfile =~ [^[:space:]] ]] ; then #empty line exception
continue # empty line exception
fi
if [[ ! $oldfile =~ [^[:space:]] ]] ; then #empty line exception
continue # empty line exception
fi
echo Comparing $updatedfile with $oldfile
if diff "$updatedfile" "$oldfile" >/dev/null ; then
echo The files compared are the same. No changes were made.
else
echo The files compared are different.
cp -f -v $oldfile /infanass/dev/admin/backup/`uname -n`_${oldfile##*/}_$(date +%F-%T)
cp -f -v $updatedfile $oldfile
fi
done
done 3</infanass/dev/admin/servers.txt
I keep on getting this error and the ssh doesn't actually connect and perform the code on the server its suppose to be ssh'd on.
Pseudo-terminal will not be allocated because stdin is not a terminal
I feel like everything the guy above just said is so wrong.
Expect?
It's simple:
ssh -i ~/.ssh/bobskey bob#10.10.10.10 << EOF
echo I am creating a file called Apples in the /tmp folder
touch /tmp/apples
exit
EOF
Everything in between the 2 "EOF"s will be run in the remote server.
The tags need to be the same. If you decide to replace "EOF" with "WayneGretzky", you must change the 2nd EOF also.
You seem to assume that when you run ssh to connect to a server, the rest of the commands in the file are passed to the remote shell running in ssh. They are not; instead they will be processed by the local shell once ssh terminates and returns control to it.
To run remote commands through ssh there are a couple of things you can do:
Write the commands you want to execute to a file. Copy the file to the remote server using scp, and execute it with ssh user#remote command
Learn a bit of TCL and use expect
Write the commands in a heredoc, but be careful with variable substitution: substitution happens in the client, not on the server. For example this will output your local home directory, not the remote:
ssh remote <<EOF
echo $HOME
EOF
To make it print the remote home directory you have to use echo \$HOME.
Also, remember that data files such as filelist.txt have to be explicitly copied if you want to read them on the remote side.

Resources