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

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

Related

While loop in bash script execute only once [duplicate]

This question already has answers here:
bash cycle breaks when calling ssh command in the loop
(2 answers)
Closed 3 months ago.
I have a given shell script:
#!/bin/bash
USERNAME=xenobot
SSH_KEY_LOCATION="~/.ssh/id_rsa"
FILE="server.info"
while read -r server_ip package_type; do
if [[ $package_type == "deb" ]]; then
echo "For $server_ip package type is $package_type"
DPKG_FILE_PATH=$(ls ./src/dpkg/dpkg-*)
DPKG_FILE=$(basename $DPKG_FILE_PATH)
echo "$DPKG_FILE located at $DPKG_FILE_PATH will be transfered via SSH to server"
scp ./src/dpkg/${DPKG_FILE} $USERNAME#$server_ip:/tmp
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -l ${USERNAME} ${server_ip} "cd /tmp; sudo dpkg -i *.deb"
elif [[ $package_type == "rpm" ]]; then
echo "For $server_ip package type is $package_type"
RPM_FILE_PATH=$(ls ./src/rpm/rpm-*)
RPM_FILE=$(basename $RPM_FILE_PATH)
echo "$RPM_FILE located at $RPM_FILE_PATH will be transfered via SSH to server"
scp -o 'StrictHostKeyChecking no' ./src/rpm/${RPM_FILE} $USERNAME#$server_ip:/tmp
echo "$RPM_FILE has been successfuly transfered to server!"
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -l ${USERNAME} ${server_ip} "cd /tmp; sudo rpm -ivh --force $RPM_FILE"
fi
done <"$FILE"
And I have following file server.info, from which previous shell script reads:
213.136.80.123 rpm 213.136.91.102 rpm
The problem with it, as it is execute only once from first line, and just stops there without executing the second line
I have tried just to print output from this server.info using the same while read -r and it seems that outputs just fine like this
#!/bin/bash
USERNAME=xenobot
SSH_KEY_LOCATION="~/.ssh/id_rsa"
FILE="server.info"
while read -r server_ip package_type; do
if [[ $package_type == "deb" ]]; then
echo "For $server_ip package type is $package_type"
elif [[ $package_type == "rpm" ]]; then
echo "For $server_ip package type is $package_type"
fi
done <"$FILE"
Output:
213.136.80.123 rpm 213.136.91.102 rpm
So it seems like it outputs two separate lines, however I do not know why it runs script only once in bigger example
Try using a different file descriptor for your list other than 0, 1, or 2 (in this case it's set to 3).
while read -r server_ip package_type <&3; do
...
done 3<"$FILE"
If that works, then what's going on here is that ssh -- being an interactive program -- is sucking up your STDIN input.

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

BASH Script passing variable with space to Heredoc?

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.

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.

Proper way to show which SSH commands you are running

I am trying to pass an array of commands to a NID device via SSH, then storing the output into a variable. I can't figure out a efficient way to display which command is running in the output.
I can get it working by looping the the array and doing 7 separate SSH sessions. Which is very slow.
n_info=$(sshpass -p "-PW-" ssh -q -o StrictHostKeyChecking=false admin#$nid_ip << EOF
${c_array[0]}
${c_array[1]}
${c_array[2]}
${c_array[3]}
${c_array[4]}
${c_array[5]}
${c_array[6]}
exit
EOF
)
echo "$i"
echo "$n_info"| sed "s/ACCEDIAN:>//g"
Expected:
[show log]
log text
log text
log text
[show config]
config text
config text
config text
Actual:
log text
log text
log text
config text
config text
config text
Commands are not static**
Simplest/cleanest solution, if it's possible, is to run the echo commands on the nid.
Assuming headers contain no shell special characters:
sshpass -p "-PW-" ssh -q -o StrictHostKeyChecking=false admin#$nid_ip << EOF
echo '[${h_array[0]}]'; ${c_array[0]}
echo '[${h_array[1]}]'; ${c_array[1]}
echo '[${h_array[2]}]'; ${c_array[2]}
echo '[${h_array[3]}]'; ${c_array[3]}
echo '[${h_array[4]}]'; ${c_array[4]}
echo '[${h_array[5]}]'; ${c_array[5]}
echo '[${h_array[6]}]'; ${c_array[6]}
exit
EOF | sed "s/ACCEDIAN:>//g"
If you are using openssh as the ssh client, it has options to use a single control connection. Something like:
REMOTE="admin#$nid"
CP_DIR="/tmp/sshctl/$$"
CP="$CP_DIR/"%L-%r#%h:%p"
mkdir -p "$CP_DIR"
sshpass -p "-PW-" \
ssh -q -nNf -o ControlMaster=yes -o ControlPath="${CP}" \
-q -o StrictHostKeyChecking=false "$REMOTE"
# !!! check you have a connection !!!
# it may work to wrap "sshpass ..." with "if ! sshpass ...; then do_error; fi"
(
for i in {0..6}; do
echo "[$h_array[$i]}]"
ssh -o ControlPath="${CP}" \
-q -o StrictHostKeyChecking=false "$REMOTE" "${c_array[$i]}"
done
ssh -O exit -o ControlPath="$CP" "$REMOTE"
) | sed "s/ACCEDIAN:>//g"
rm -r "$CP_DIR"

Resources