I have the following code in bash:
check_port()
{
local host=${1}
local port=${2}
echo $host
echo $port
while true; do
if nc -w 5 -z 127.0.0.1 111 && nc -w 5 -z 127.0.0.1 5001 ;
then
echo -e "\a\n => Port at host is open"
break
else
echo -e "\a\n => Port at host is closed"
break
fi
}
For some reason, I get the following error:
syntax error near unexpected token `}'
`}'
I don't understand why: } is closing the scope of the function.
You need a done line to terminate your while loop, between the fi line and the closing brace }.
However, I'm not entirely certain why you even have the while true loop since you break out of it regardless of whether the if evaluates to true or false. Hence it's superfluous, unless you plan on changing the behaviour of one of those blocks at some point.
If you're not planning that, you're better off with the much simpler:
check_port()
{
local host=${1}
local port=${2}
echo $host
echo $port
if nc -w 5 -z 127.0.0.1 111 && nc -w 5 -z 127.0.0.1 5001 ; then
echo -e "\a\n => Port at host is open"
else
echo -e "\a\n => Port at host is closed"
fi
}
I'm also not entirely certain of the sanity of passing in the host and port and then ignoring them (instead using localhost with two hard-coded ports).
However, I'm going to assume that you're still in the development/testing phase rather than the possibility you've gone insane :-)
The done at the end of the while loop is missing
....
break
fi
done;
}
Related
I'm trying to answer a command prompt for the user within the script.
Command
ufw enable
Prompt
Command may disrupt existing ssh connections. Proceed with operation (y|n)?
Piece of code that I'm using but its not working properly:
function awk_ufw {
ufw status | grep Status | awk '{print $2}'
}
.
.
.
local CHECK_STATUS=$(awk_ufw)
if [ $CHECK_STATUS == "active" ]
then
echo "----------------------------"
echo "Firewall is already enabled!"
echo "----------------------------"
else
read -p "Before you enable the firewall, please ensure that at least inbound port for your ssh connection is open, otherwise you'll be locked out if ssh is the only way to access the system! [Please type \"understood\" to continue]: " UNDERSTOOD_PROMPT
if [[ $UNDERSTOOD_PROMPT == "understood" || $UNDERSTOOD_PROMPT == "UNDERSTOOD" ]]
then
ufw enable <EOF
y
EOF
if [ $? == "0" ]
then
echo "-----------------"
echo "Firewall enabled!"
echo "-----------------"
else
echo "------------------------------------------------------------------------------------------------------"
echo "Something went wrong during the process, unsure whether firewall was enabled, please recheck manually!"
echo "------------------------------------------------------------------------------------------------------"
fi
else
echo "------------------------------------------------------"
echo "Skipping the step since \"Understood\" wasn't entered!"
echo "------------------------------------------------------"
fi
fi
Code break on
ufw enable <EOF
y
EOF
Before you enable the firewall, please ensure that at least inbound port for your ssh connection is open, otherwise you'll be locked out if ssh is the only way to access the system! [Please type "understood" to continue]: understood
./script.sh: line 84: EOF: No such file or directory
./script.sh: line 85: y: command not found
./script.sh: line 86: EOF: command not found
------------------------------------------------------------------------------------------------------
Something went wrong during the process, unsure whether firewall was enabled, please recheck manually!
------------------------------------------------------------------------------------------------------
Any suggestions how to correct it/improve it?
Thank you in advance!
The end marker in heredocs cannot be indented:
cat << MARKER
content
MARKER
works, while:
cat << MARKER
content
MARKER
doesn't.
And heredocs uses two lt symbols: << MARKER. < thing is a redirection.
In your case it would be:
if [[ $UNDERSTOOD_PROMPT == "understood" || $UNDERSTOOD_PROMPT == "UNDERSTOOD" ]]
then
ufw enable <<EOF
y
EOF
But you might consider piping echo y | instead:
echo y | ufw enable
Which is effectually the same in your case.
FYI awk can do pattern matching, to there is no need to you to pipe grep | awk:
awk_ufw() {
ufw status | awk '/Status/ {print $2}'
}
Or possible just check the first field:
awk_ufw() {
ufw status | awk '$1 == "Status" {print $2}'
}
A quick scan of the man page shows you can avoid the problem all together:
By default, ufw will prompt when enabling the firewall while running
under ssh. This can be disabled by using 'ufw --force enable'.
So using:
ufw --force enable
in your script avoids the issue of having to pass a keypress in.
First time post, please forgive any missing information.
I have a script that is supposed to work with icinga. I need icinga to log into my Linux box and run a command like "script ". The script will then run a command to that hostname like sudo /etc/init.d/apache2 status then report back "running or unused" and an exit status of 0 or 2.
I'm wondering how I could add another command and have it one or the other run depending on what hostname it's given. Half of them need apache2 to be running and the other half need to have a process called dss to be running. I'd rather not have two separate scripts. Here is the working script and sorry it's sloppy but I haven't done any clean up and I'm not real good at bash yet.
so the user would run the script ./chkdss2 or
#!/bin/bash
ec=0
ec1=2
var3=run
var4=unused
for host in "$#"
do
var1=`ssh $host sudo /etc/init.d/dss status|awk '{print $6}'`
var2="$( echo $var1 | cut -c 3-5 )"
if [[ "$var2" == "$var3" ]]; then
echo "$host is running"
echo $ec
else
echo "$host is not running"
echo $ec1
fi
done
There are a couple ways to test if a particular hostname is for apache or dss. You only need to have a list of hostnames for each case, and check if the received hostnames are included in said lists.
Method 1: using arrays
#!/bin/bash
# Method 1, using array lists of hosts
apachehosts=('ap1' 'ap2' 'ap3')
dsshosts=('dss1' 'dss2' 'dss3')
for host in "$#"
do
if printf '%s\n' "${apachehosts[#]}" | grep -Fxq "$host"
then
echo "$host: APACHE HOST"
elif printf '%s\n' "${dsshosts[#]}" | grep -Fxq "$host"
then
echo "$host: DSS HOST"
else
echo "ERROR, $host: unknown host"
fi
done
To modify the lists of hosts, simply add or remove values in the declaration of arrays apachehosts and dsshosts.
Method 2: using case
#!/bin/bash
# Method 2, using case
for host in "$#"
do
case "$host" in
'ap1'|'ap2'|'ap3')
echo "CASE, $host: APACHE HOST"
;;
'dss1'|'dss2'|'dss3')
echo "CASE, $host: DSS HOST"
;;
*)
echo "ERROR CASE, $host: unknown host"
;;
esac
done
Here, you edit the patterns in each case.
Method 3: using if
#!/bin/bash
# Method 3, using if
for host in "$#"
do
if [[ "$host" == 'ap1' || "$host" == 'ap2' || "$host" == 'ap3' ]]
then
echo "IF, $host: APACHE HOST"
elif [[ "$host" == 'dss1' || "$host" == 'dss2' || "$host" == 'dss3' ]]
then
echo "IF, $host: DSS HOST"
else
echo "IF, $host: unknown host"
fi
done
Here you modify the if conditions. I prefer the other methods, since this one is more complicated to edit, it is not as clear, especially if your list of hosts is long.
Method 4: condition on the hostnames
If you are lucky, there is some pattern to your hostnames. Ex. all apache servers start with letters ap, all your dss servers include dss in the name, ...
You can then simply use 2 if statements to decide which is which.
#!/bin/bash
# Method 4, patterns
for host in "$#"
do
if [[ $(echo "$host" | grep -c -e "^ap") -ne 0 ]]
then
echo "PATTERNS, $host: APACHE HOST"
elif [[ $(echo "$host" | grep -c -e "dss") -ne 0 ]]
then
echo "PATTERNS, $host: DSS host"
else
echo "PATTERNS, $host: unknown host"
fi
done
Note: hostname apdss1 would come out as an Apache server here. Previous methods would respond "unknown host". You patterns must be strict enough to avoid mismatches.
I had a similar task to get few report items using single ssh request.
I had to retrieve in singel ssh command:
Full hostname (FQDN)
Linux version
IP address of its Docker host if exist, or "none"
I got my script to work in 3 stage.
1. Get multiple lines of information from remote host
ssh -q dudi-HP-Compaq-Elite-8300-MT <<< '
date +%F:%T # line 1: time stamp
hostname -f # line 2: hostname
awk "/DESCR/{print \$3}" /etc/lsb-release # line 3 : host linux distribution version
ip a | awk "/inet / && !/127.0.0.1/{sub(\"/.*\",\"\",\$2);printf(\"%s \", \$2)}" # line 4: list IP address to the host
'
Results:
2022-03-05:22:22:21
dudi-HP-Compaq-Elite-8300-MT
20
192.168.2.111 192.168.122.1 172.17.0.1
2. Process multiple lines of information from remote host
Read lines of information from remote host, into an array sshResultsArr.
readarray -t sshResultsArr < <(ssh -q dudi-HP-Compaq-Elite-8300-MT <<< '
date +%F:%T # line 1: time stamp
hostname -f # line 2: hostname
awk "/DESCR/{print \$3}" /etc/lsb-release # line 3 : host linux distribution version
ip a | awk "/inet / && !/127.0.0.1/{sub(\"/.*\",\"\",\$2);printf(\"%s \", \$2)}" # line 4: list IP address to the host
')
hostname=${sshResultsArr[1]}
osVersion=${sshResultsArr[2]}
hasDockerIp=$(grep -Eo "172(.[[:digit:]]{1,3}){3}" <<< "${sshResultsArr[3]}") # find IP starting with 172
hasDockerIp=${hasDockerIp:="none"} # if not found IP set to "NONE"
printf "%s \t OS version: %s \t has Docker IP: %s\n" "$hostname" "$osVersion" "$hasDockerIp"
Result:
dudi-HP-Compaq-Elite-8300-MT OS version: 20 has Docker IP: 172.17.0.1
3. Process each remote host in a loop
#!/bin/bash
for host in "$#"; do
readarray -t sshResultsArr < <(ssh -q $host <<< '
date +%F:%T # line 1: time stamp
hostname -f # line 2: hostname
awk "/DESCR/{print \$3}" /etc/lsb-release # line 3 : host linux distribution version
ip a | awk "/inet / && !/127.0.0.1/{sub(\"/.*\",\"\",\$2);printf(\"%s \", \$2)}" # line 4: list IP address to the host
')
hostname=${sshResultsArr[1]}
osVersion=${sshResultsArr[2]}
hasDockerIp=$(grep -Eo "172(.[[:digit:]]{1,3}){3}" <<< "${sshResultsArr[3]}") # find IP starting with 172
hasDockerIp=${hasDockerIp:="none"} # if not found IP set to "NONE"
printf "%s \t OS version: %s \t has Docker IP: %s\n" "$hostname" "$osVersion" "$hasDockerIp"
done
I was able to take a little bit from the answers I received and put together something that works well. Thank you all for your answers.
for host in "$#"
do
case "$host" in
('vho1uc1-primary'|'vho1uc2-backup'|'vho2uc1-primary'|'vho2uc2-backup'|'vho3uc1-primary'|'vho3uc2-backup'|'vho10uc1-primary')
var1=`ssh "$host" sudo /etc/init.d/apache2 status|awk '{print $4}'`
var2="$( echo $var1 | cut -c 3-5 )"
if [[ "$var2" == "$var3" ]]; then
echo "Apache2 on $host is running"
echo "0"
else
echo "Apache2 on $host is not running"
echo "2"
fi
;;
*)
esac
done
I want to use fping to ping multiple ips contained in a file and output the failed ips into a file i.e.
hosts.txt
8.8.8.8
8.8.4.4
1.1.1.1
ping.sh
#!/bin/bash
HOSTS="/tmp/hosts.txt"
fping -q -c 2 < $HOSTS
if ip down
echo ip > /tmp/down.log
fi
So I would like to end up with 1.1.1.1 in the down.log file
It seems that parsing the data from fping is somewhat difficult. It allows the parsing of data for hosts that is alive but not dead. As a way round the issue and to allow for multiple host processing simultaneously with -f, all the hosts that are alive are placed in a variable called alive and then the hosts in the /tmp/hosts.txt file are looped through and grepped against the variable alive to decipher whether the host is alive or dead. A return code of 1 signifies that grep cannot find the host in alive and hence an addition to down.log.
alive=$(fping -c 1 -f ipsfile | awk -F: '{ print $1 }')
while read line
do
grep -q -o $line <<<$alive
if [[ "$?" == "1" ]]
then
echo $line >> down.log
fi
done < /tmp/hosts.txt
Here's one way to get the result you want. Note however; i didn't use fping anywhere in my script. If the usage of fping is crucial to you then i might have missed the point entirely.
#!/bin/bash
HOSTS="/tmp/hosts.txt"
declare -i DELAY=$1 # Amount of time in seconds to wait for a packet
declare -i REPEAT=$2 # Amount of times to retry pinging upon failure
# Read HOSTS line by line
while read -r line; do
c=0
while [[ $c < $REPEAT ]]; do
# If pinging an address does not return the word "0 received", we assume the ping has succeeded
if [[ -z $(ping -q -c $REPEAT -W $DELAY $line | grep "0 received") ]]; then
echo "Attempt[$(( c + 1))] $line : Success"
break;
fi
echo "Attempt[$(( c + 1))] $line : Failed"
(( c++ ))
done
# If we failed the pinging of an address equal to the REPEAT count, we assume address is down
if [[ $c == $REPEAT ]]; then
echo "$line : Failed" >> /tmp/down.log # Log the failed address
fi
done < $HOSTS
Usage: ./script [delay] [repeatCount] -- 'delay' is the total amount of seconds we wait for a response from a ping, 'repeatCount' is how many times we retry pinging upon failure before deciding the address is down.
Here we are reading the /tmp/hosts.txt line by line and evaluating each adress using ping. If pinging an address succeeds, we move on to the next one. If an address fails, we try again for as many times as the user has specified. If the address fails all of the pings, we log it in our /tmp/down.log.
The conditions for checking whether a ping failed/succeeded may not be accurate for your use-cases, so maybe you will have to edit that. Still, i hope this gets the general idea across.
I am trying to wait my docker container is up to start some commands.
i am doing the following :
#!/bin/bash
DOCKER_IP=192.168.99.100
ES_PORT=9300
docker-compose up -d
while [ -z "$(nc -z $DOCKER_IP $ES_PORT)" ]; do
sleep 1
done
echo "Do some stuff now it's up !"
I already check with cat -e the return of nc -z is empty, without the quotes, with [ ! -n $(nc ...)] as conditional expression ...
When the return of nc is not empty, i have Connection to 192.168.99.100 port 9300 [tcp/vrace] succeeded! in loop and it never exit.
Why this simple loop condition is not working ?
The problem is most probably because nc prints the message "Connection to 192.168.99.100 port 9300 [tcp/vrace] succeeded!" on the standard error rather than standard output (otherwise you shouldn't see it). You can redirect standard error (so that it is captured by the command substitution) as follows:
while [ -z "$(nc -z $DOCKER_IP $ES_PORT 2>&1)" ]; do
sleep 1
done
However, not all versions of nc print such a message when connection succeeds (mine doesn't). So why don't you simply use the exit status of nc -z:
while ! nc -z $DOCKER_IP $ES_PORT
do
sleep 1
done
I'm writing a file transfer script and it gets pretty complex. So in the beginning when I generate my IP address to transfer from, I want to validate that I can indeed connect to it. The code in the area looks like this:
USER_ID=$1
if [[ $GROUP == "A" ]]; then
ADDRESS="${USER_ID}#morgan.company.gov"
elif [[ $GROUP == "B" ]]; then
ADDRESS="${USER_ID}#mendel.company.gov"
else
log_msg fatal "Couldn't resolve group $GROUP. Exiting"
exit 1;
fi
// HERE I want to test that $ADDRESS exists, and I can connect right now I
// have what is below. I just think there is a better way to do it
ssh -q $ADDRESS exit
if [ $? != 0 ]; then
log_msg fatal "Couldn't resolve host, do you have login privileges with $ADDRESS"
fi
... // lots of other things happen
scp $ADDRESS:$INCOMING_FILE $NEW_FILE
What I have works, but it doesn't seem like its an elegant solution. I'd prefer not to actually ssh and exit the server, just test the connection.
You can use this shell function that tests wether the host has the ssh port open or not:
#!/bin/bash
function isUp(){
local ip=$1
local sshport=22
if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
if [[ $(nmap -P0 $ip -p$sshport | grep ^$sshport | cut -d' ' -f2) == "open" ]]; then
return 0
else
return 1
fi
fi
}
if isUp $1; then
ssh $1 uptime
else
echo "Host $1 is not available"
fi
or make use of this (new to me) bash functionality:
#!/bin/bash
function isUp(){
local ip=$1
if echo > /dev/tcp/$ip/22 >/dev/null 2>&1; then
return 0
else
return 1
fi
}
If you want to thoroughly check for the connection then you are better off analyzing the return codes from an scp connection. This will allow you to understand the reason why your connection failed and act in consequence (there may be various reasons at different levels, from a general lack of connectivity to issues with the keys for instance).
If you are just interested in a binary answer ("is my connection OK as it is?") then your code is fine, though I would directly use scp.
There is nothing wrong with your line:
ssh -q $ADDRESS exit
This is the best/fastest way to test a connection.