Iteration is not working - bash

I am trying to get this script to work. It works fine when I execute it locally but it's not iterating through IP file for remote servers list in the file.
#!/bin/bash
entry=$(cat IPfile)
for i in $entry
do
ssh -q "$entry"
if [[ -n $(egrep "Red Hat Enterprise Linux Server release 5" /etc/redhat-release) ]]; then
sed -i 's/#includedir/##includedir/' /etc/sudoers
fi
done

Are you sure you don't mean ssh -q "$i"? $entry presumably expands to many values.
Edit
I assume you want the grep/sed to occur on each server in the ip list. What your script does is to ssh into each server, then wait for instructions. This should help.
#!/bin/bash
for ip in $(<IPfile); do
# Tell the remote server to start bash, but since its
# standard input is not a TTY it will start bash in
# noninteractive mode.
ssh -q "$ip" bash <<-SSH
if [ ! -r /etc/redhat-release ]; then
printf 'ip "%s" did not have a redhat-release file.\n' "$ip"
elif fgrep -q 'Red Hat Enterprise Linux Server release 5' /etc/redhat-release; then
sed -i 's/#includedir/##includedir/' /etc/sudoers
else
printf 'ip "%s" was not rhel server 5.\n' "$ip"
fi
SSH
done
Some distros don't remove /etc/redhat-release file and make it as part of their own release package. If the script is intended to run strictly on RHEL5, check the version of redhat-release package instead.
The conditional statement will then be:
if version=$(rpm -q --qf "%{version}" redhat-release); then
# it is RHEL
if [ ${version:0:1} == 5 ]; then
# it is RHEL 5
fi
fi

It's because the ssh -q command is "eating" your list. You should add the "-n" option to ssh to prevent it from getting your list from the standard input.

Related

How to change name of file if already present on remote machine?

I want to change the name of a file if it is already present on a remote server via SSH.
I tried this from here (SuperUser)
bash
ssh user#localhost -p 2222 'test -f /absolute/path/to/file' && echo 'YES' || echo 'NO'
This works well with a prompt, echoes YES when the file exists and NO when it doesn't. But I want this to be launched from a crontab, then it must be in a script.
Let's assume the file is called data.csv, a condition is set in a loop such as if there already is a data.csv file on the server, the file will be renamed data_1.csv and then data_2.csv, ... until the name is unique.
The renaming part works, but the detection part doesn't :
while [[ $fileIsPresent!='false' ]]
do
((appended+=1))
newFileName=${fileName}_${appended}.csv
remoteFilePathname=${remoteFolder}${newFileName}
ssh pi#localhost -p 2222 'test -f $remoteFilePathname' && fileIsPresent='true' || fileIsPresent='false'
done
always returns fileIsPresent='true' for any data_X.csv. All the paths are absolute.
Do you have any idea to help me?
This works:
$ cat replace.sh
#!/usr/bin/env bash
if [[ "$1" == "" ]]
then
echo "No filename passed."
exit
fi
if [[ ! -e "$1" ]]
then
echo "no such file"
exit
fi
base=${1%%.*} # get basename
ext=${1#*.} # get extension
for i in $(seq 1 100)
do
new="${base}_${i}.${ext}"
if [[ -e "$new" ]]
then
continue
fi
mv $1 $new
exit
done
$ ./replace.sh sample.csv
no such file
$ touch sample.csv
$ ./replace.sh sample.csv
$ ls
replace.sh
sample_1.csv
$ touch sample.csv
$ ./replace.sh sample.csv
$ ls
replace.sh
sample_1.csv
sample_2.csv
However, personally I'd prefer to use a timestamp instead of a number. Note that this sample will run out of names after 100. Timestamps won't. Something like $(date +%Y%m%d_%H%M%S).
As you asked for ideas to help you, I thought it worth mentioning that you probably don't want to start up to 100 ssh processes each one logging into the remote machine, so you might do better with a construct like this that only establishes a single ssh session that runs till complete:
ssh USER#REMOTE <<'EOF'
for ((i=0;i<10;i++)) ; do
echo $i
done
EOF
Alternatively, you can create and test a bash script locally and then run it remotely like this:
ssh USER#REMOTE 'bash -s' < LocallyTestedScript.bash

Try to connect via script to multiple servers

I am trying to connect to multiple servers from file (servers.txt --> something around 300 IP add) and some server is RHEL5, some RHEL7 (so I must use a diff command).
I can connect to multiple server, its OK, but I can't continue with some condition: like if you don't know one command use another command.
#!/bin/bash
for host in $(cat servers.txt); do
ssh -q -o StrictHostKeyChecking=no root#$host
#when I try to continue with "if" -> of course I am logout from servers
I agree with #rkosegi that this can be achieved an Ansible ad-hoc command, but you'd have to convert the list of servers to an inventory - a very simple task.
At the bash prompt, I think I understand what you want. You want to try multiple commands via each ssh command. So let's assume you want to check the version of the redhat-release package and take different actions from that:
while read HOST; do
ssh -q -o StrictHostKeyChecking=no root#$HOST '
VERSION="`rpm -q --queryformat "%{VERSION}" redhat-release`"
if [[ $VERSION == 5* ]]; then # RHEL5
echo this is a rhel5
elif [[ $VERSION == 7* ]]; then # RHEL7
echo this is a rhel7
else
echo this is neither a rhel5 or rhel7
fi
'
done <servers.txt
Of course, the script can be written on one line, but I thought I'd format it nicer here for increased readability.
Note: The post being tagged with bash, the above command also uses [[ tests which are specific to bash and won't work on some other shells.

Multiline SSH remote script

I have been trying for 2 hours to rewrite this SSH remotelly executed command to multiline form to be more readable, but everytime I have something wrong. I tried multiple ways as described in similar questions but I just suck at BASH. I am trying to do
echo "Deploying $1 to remote"
sudo ssh -i ../keys/key.pem username#$2 '
id=$(docker ps -a -q -f name=$1); if [ -n "$id" ]; then docker rm --force $1; fi;
docker run -d --network host -v /var/log/$1/:/var/log/$1/
-e SERVER_PORT=80
--name $1 username/image:$1'
I want to run docker image remotelly with some arguments created remotelly ($id) and some expanded locally before executing the script ($1, $2).
You will probably find this easier to write if you write it as a standalone script:
#!/bin/sh
container="$1"
id=$(docker ps -aq -f name="$container")
if [ -n "$id" ]; then
...
fi
...
Run this by hand on the target system to make sure it does what you want.
Once you have that, you can copy the script and run it in two separate commands:
echo "Deploying $1 to remote"
scp -i ../keys/key.pem launch-container.sh "username#$2:/tmp/launch-container.sh"
ssh -i ../keys/key.pem "username#$2" sh /tmp/launch-container.sh "$1"
You also might look into various system-automation tools that are purpose-built for this kind of task. I'm partial to Ansible for not requiring a dedicated server and working over ssh in the same way you're showing; it has a set of Docker-related commands that can do this set of tasks fairly directly.

Alternative in CentOS 7 for "nc -z"

I have a bash program that checks that a daemon in a given port is working:
nc -z localhost $port > /dev/null
if [ "$?" != "0" ]
then
echo The server on port $port is not working
exit
fi
This program works perfectly in CentOS 6. However, it seems that CentOS 7 has changed the underlying implementation for nc command (CentOS 6 seems to use Netcat and CentOS 7 uses another thing called Ncat) and now the -z switch doesn't work:
$ nc -z localhost 8080
nc: invalid option -- 'z'
Looking to the man nc page in CentOS 7 I don't see any clear alternative to -z. Any suggestion on how I should fix my bash program to make it work in CentOS 7?
And a trimmed down version for what you really want:
#!/bin/bash
# Start command: nohup ./check_server.sh 2>&1 &
check_server(){ # Start shell function
checkHTTPcode=$(curl -sLf -m 2 -w "%{http_code}\n" "http://10.10.10.10:8080/" -o /dev/null)
if [ $checkHTTPcode -ne 200 ]
then
# Check failed. Do something here and take any corrective measure here if nedded like restarting server
# /sbin/service httpd restart >> /var/log/check_server.log
echo "$(date) Check Failed " >> /var/log/check_server.log
else
# Everything's OK. Lets move on.
echo "$(date) Check OK " >> /var/log/check_server.log
fi
}
while true # infinite check
do
# Call function every 30 seconds
check_server
sleep 30
done
according this post, I used:
nc -w1 localhost $port < /dev/null
( this work on debian too, but it's slower than nc -z )

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