Proper way to show which SSH commands you are running - bash

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"

Related

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.

trying to make sense of a bash script

The script is supposed to distribute keys from an ossec server to it's clients
cat /usr/local/bin/dist-ossec-keys.sh
#!/bin/sh
#
for host in chef-production2-doctor-simulator01
do
echo "host is $host"
key=`mktemp`
grep $host /var/ossec/etc/client.keys > $key
echo "key is $key"
scp -i /var/ossec/.ssh/id_rsa -B -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $key ossecd#$host:/var/ossec/etc/client.keys >/dev/null 2>/dev/null
rm $key
ech "done"
done
I ran the script line by line and it's output is
host is chef-production2-doctor-simulator01
key is /tmp/tmp.fAZqvNkJ9f
The bash script is created from this template
cat /var/ossec/etc/client.keys
001 #*#*#*#*#*#*#*#*#*#*#197.221.226 7196c76c568258e2ad836f8e1aa37e0758dee969f560ceb59be76879c3f3412d
002 test-agent 10.128.0.9 e2c9b117f26a202598007dcb4ec64e01f18000f9d820f6b3508a95e5313e6537
what is it supposed to do ?
why is it not working?
The error was in the scp -i /var/ossec/... line
My poor linux knowledge failed to notice the
>/dev/null 2>/dev/null
removing that revealed the real error (which was Permission denied (publickey))

printf in bash sometimes prints new line, can't figure out why

if i have a file with below contents:
127.0.0.1
127.0.0.2
127.0.0.3
8.8.8.8
127.0.0.4
and run this command to quickly test SSH connection success or failure:
while read host
do
ssh -n -q -o BatchMode=yes -o ConnectTimeout=5 $host "echo 2>&1" && \
printf "%-15s%-45s%s" `id -un` $host "SSH_OK" || \
printf "%-15s%-45s%s" `id -un` $host "SSH_BAD"
done <host_list
I get:
myname 127.0.0.1 SSH_OK
myname 127.0.0.2 SSH_OK
myname 127.0.0.3 SSH_OKmyname 8.8.8.8 SSH_BAD
myname 127.0.0.4 SSH_OK[myname#server ~]$
As you can see, it looks nasty because it doesn't print a newine for the SSH_BAD output or the last line so bash prompt is tagged to end of last check.
I can fix this by adding a \n to the prinf but then i have double spaces on everything except for the BAD line.
Can someone tell me please where is printf getting the newline from when i don't specify one, and why it prints one for the SSH_OK lines but not the SSH_BAD lines.
Is there a good/easy way to fix this?
thank you
fLo
The newlines are coming from echo 2>&1 that you're running on the remote system. When the ssh is successful, this command executes, and you get a newline printed before the printf. When the ssh fails, the command doesn't run, so you get no newline before the printf.
I suggest you use a command that doesn't display anything, and then put \n in both printf lines:
while read host
do
ssh -n -q -o BatchMode=yes -o ConnectTimeout=5 $host "true" && \
printf "%-15s%-45s%s\n" `id -un` $host "SSH_OK" || \
printf "%-15s%-45s%s\n" `id -un` $host "SSH_BAD"
done <host_list

Resources