BASH Script passing variable with space to Heredoc? - bash

I'm using the following:
filename="Test File 17-07-2020.xls"
sshpass -p $password ssh root#$IP /bin/bash -s "$filename" << 'EOF'
echo $1
EOF
This works when filename equals Testfile.xls and the echo outputs the full filename.
But fails if the filename is called Test File 17-07-2020.xls
My understanding is the spaces are breaking the input so it becomes:
$1 = Test
$2 = File
$3 = 17-07-2020.xls
Is there anyway to pass this keeping the spaces and having it all in $1
If I add filename=$(echo "$filename" | sed 's/ /\\ /g') before the SSHPASS command it does work.
Is that a valid way to do it or is there a better way ?
Thanks

You still need to quote $1. Quoting the delimiter prevents $1 from being expanded early; it doesn't prevent word splitting once the shell actually executes echo $1.
sshpass -p $password ssh root#$IP "/bin/bash -s \"$filename\"" << 'EOF'
echo "$1"
EOF
If you are using bash locally, there are two extensions that could produce a value that is safe to pass to the remote shell.
sshpass -p $password ssh root#$IP "/bin/bash -s $(printf '%q' "$filename")"
or
sshpass -p $password ssh root#$IP "/bin/bash -s ${filename#Q}"
The former works at least in bash 3.2 (I highly doubt you are using an older version), while the latter requires bash 4.4.

Related

Calling local variable in remote host

below script is not working in remote host
PARAMETER="123 456"
ssh user#host PARAMETER="$PARAMETER" bash -s <<- __EOF
echo \$PARAMETER
__EOF
but when I try Below it is working fine
PARAMETER="123"
ssh user#host PARAMETER="$PARAMETER" bash -s <<- __EOF
echo \$PARAMETER
__EOF
Looks like it is not accepting space in the variable. Can anyone help?
As locally, you have to quote variable expansion to disable word splitting. echo "$var" not echo $var.
And you have to quote the value for unquoting by ssh:
ssh user#host PARAMETER="$(printf "%q" "$PARAMETER")" bash -s <<- __EOF
echo "\$PARAMETER"
__EOF
If you are using stdin, I suggest to use to transfer all context with stdin without caring of super-double-quoting:
# define function with the code you want to run, normally quoted
work() {
echo "$PARAMETER"
}
ssh user#host bash -s <<EOF
$(declare -p PARAMETER) # serialize variables
$(declare -f work) # serialize functions
work # run the function
EOF

How to launch a script for a list of ip addresses (bash script)

i have a problem with my bash shell script. This is my code:
#!/bin/bash/
while read line
do
echo -e "$line
"
sleep 5;
done < Ip.txt
sshpass -p 'Password' ssh -o StrictHostKeyChecking=no root#$ip -t "cd /exemple/ && sh restart-launcher.sh;exec \$SHELL -l"
My script allows to launch for each ip (Ip.txt) in the folder "exemple" a script(restart-launcher.sh) but when I launch it , it only lists the ip without taking this part into account:
sshpass -p 'Password' ssh -o StrictHostKeyChecking=no root#$ip -t "cd /folders/ && sh restart-launcher.sh;exec \$SHELL -l"
How do I create a bash script that works in Linux?
#!/bin/bash/
while read -r line; do
echo -e "$line\n"
sshpass -p 'Password' ssh -o StrictHostKeyChecking=no root#$line -t \
"cd /exemple/ && sh restart-launcher.sh;exec \$SHELL -l"
sleep 5
done < Ip.txt
Now, we could have a discussion about using sshpass (or rather, why you shouldn't), but it feels out of scope.
So, all commands you wish to be looped over need to be inside the loop.
As you read sets the variable $line, that is what you need to use. In your example, you used the variable $ip which you haven't set anywhere.
If we assume that "Ip.txt" contains a list of only IPs perhaps what you mean to do is use sshpass inside the while-loop so it runs for each IP in the .txt file.
#!/bin/bash/
while read line
do
echo -e "$line
"
sshpass -p 'Password' ssh -o StrictHostKeyChecking=no root#$line -t "cd /exemple/ && sh restart-launcher.sh;exec \$SHELL -l"
sleep 5;
done < Ip.txt
sshpass has been moved into the while loop and $ip replaced with $line

variables in remote nested server via ssh

Trying to set and access some variables on a remote server
the script will execute on a local server, it will login to remote-server1 and then login again to another second remote server remote-server2
I can successfully set and access variables in both local and remote-server1 without any issues, but having problem doing the same on remote-server2
local1=$(echo "local-srv"); echo "${local1}"
output=$(sshpass -p "${PSSWD}" ssh -t -q -oStrictHostKeyChecking=no admin#$mgmtIP "bash -s" <<EOF
remote1=\$(echo "remote-srv1"); echo "\${remote1}"
ssh -t -q -oStrictHostKeyChecking=no "${targetCompute}" "bash -s" <<EOF2
remote2=\$(echo "remote-srv2"); echo "\${remote2}"
EOF2
EOF
)
Here is my output
local-srv
remote-srv1
as you can see remote-srv2 is missing
----- UPDATE ---
please note that $(echo "text") is just for simplicity but a complex command will be executed here and the output set to a variable
You have two nested ssh commands with nested here-documents, and to delay interpretation of the $ expressions in the inner one, you need more escapes. To see the problem, you can replace the ssh command with cat to see what would be sent to the remote computer. Here's an example, using your original code (and some modified variable definitions); note that the $ and > are prompts from my shell.
$ targetCompute=remote-server2
$ local1="local-srv"; echo "${local1}"
local-srv
$ cat <<EOF
> remote1=\$(echo "remote-srv1"); echo "\${remote1}"
> ssh -t -q -oStrictHostKeyChecking=no "${targetCompute}" "bash -s" <<EOF2
> remote2=\$(echo "remote-srv2"); echo "\${remote2}"
> EOF2
> EOF
remote1=$(echo "remote-srv1"); echo "${remote1}"
ssh -t -q -oStrictHostKeyChecking=no "remote-server2" "bash -s" <<EOF2
remote2=$(echo "remote-srv2"); echo "${remote2}"
EOF2
Notice that the lines relating to remote1 and remote2 have both had their escapes removed, so they're both going to have their $ expressions expanded on remote-srv1. That's what you want for the remote1 line, but to delay interpretation of the remote2 line you have to add another escape... and that escape itself needs to be escaped, so there'll actually be three escapes before each $:
$ cat <<EOF
> remote1=\$(echo "remote-srv1"); echo "\${remote1}"
> ssh -t -q -oStrictHostKeyChecking=no "${targetCompute}" "bash -s" <<EOF2
> remote2=\\\$(echo "remote-srv2"); echo "\\\${remote2}"
> EOF2
> EOF
remote1=$(echo "remote-srv1"); echo "${remote1}"
ssh -t -q -oStrictHostKeyChecking=no "remote-server2" "bash -s" <<EOF2
remote2=\$(echo "remote-srv2"); echo "\${remote2}"
EOF2
So \\\$(echo "remote-srv2") and "\\\${remote2}" in the local here-document become \$(echo "remote-srv2") and "\${remote2}" in the here-document on remote-srv1, and then the command actually gets executed and the variable expanded on remote-srv2.
I needed to escape by ///
Thanks to the answer given by #Goron Davisson
local1=$(echo "local-srv"); echo "${local1}"
output=$(sshpass -p "${PSSWD}" ssh -t -q -oStrictHostKeyChecking=no admin#$mgmtIP "bash -s" <<EOF
remote1=\$(echo "remote-srv1"); echo "\${remote1}"
ssh -t -q -oStrictHostKeyChecking=no "${targetCompute}" "bash -s" <<EOF2
remote2=\\\$(echo "remote-srv2"); echo "\\\${remote2}"
EOF2
EOF
)
Output
local-srv
remote-srv1
remote-srv2

Multiline ssh command with for

I'm having a bash script that is executing commands through ssh.
FILENAMES=(
"export_production_20200604.tgz"
"export_production_log_20200604.tgz"
"export_production_session_20200604.tgz"
"export_production_view_20200604.tgz"
)
sshpass -p $PASSWORD ssh -T $LOGIN#$IP '/bin/bash' <<EOF
for f in "${FILENAMES[#]}"; do
echo Untar "$f"
done
EOF
The thing is when I execute the script, $f is empty.
I've looked at multiple solutions online to perform multiple command executions, but none works :
link 1
link 2
...
Could you help me figure it out ?
Note :
The execution of :
for f in "${FILENAMES[#]}"; do
echo Untar "$f"
done
outside the <<EOF EOF, works
On local :
bash 4.4.20(1)-release
Remote :
bash 4.2.46(2)-release
EDIT : Tricks
Having a tight timeline, and having no choice, I implemented the solution provided by #hads0m, may it helps fellow developer having the same issue :
# $1 the command
function executeRemoteCommand() {
sshpass -p $DB_PASSWORD ssh $DB_LOGIN#$DB_SERVER_IP $1
}
for i in "${!FILENAMES[#]}"; do
f=$FILENAMES[$i]
DB_NAME=$DB_NAMES[$i]
# Untar the file
executeRemoteCommand '/usr/bin/tar xzvf '$MONGODB_DATA_PATH'/'$TMP_DIRECTORY'/'$f' --strip-components=1'
# Delete the tar
executeRemoteCommand 'rm -f '$MONGODB_DATA_PATH'/'$TMP_DIRECTORY'/'$f''
# Restore the database
executeRemoteCommand 'mongorestore --host 127.0.0.1:'$DB_PORT' --username "'$MONGODB_USER'" --password "'$MONGODB_PASSWORD'" --authenticationDatabase admin --gzip "'$DB_NAME'" --db "'$DB_NAME'"'
done
You need to escape $ sign to avoid it being expanded locally and pass the array to remote.
This may be what you wanted :
#!/usr/bin/env bash
FILENAMES=(
"export_production_20200604.tgz"
"export_production_log_20200604.tgz"
"export_production_session_20200604.tgz"
"export_production_view_20200604.tgz"
)
sshpass -p $PASSWORD ssh -T $LOGIN#$IP '/bin/bash' <<EOF
$(declare -p FILENAMES)
for f in "\${FILENAMES[#]}"; do
echo Untar "\$f"
done
EOF
Try running it like this:
for f in "${FILENAMES[#]}"; do
sshpass -p $PASSWORD ssh -T $LOGIN#$IP echo Untar "$f"
done
Also, don't forget to add #!/bin/bash into the first line of your script.

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.

Resources