How to awk all lines instead of only looking at the first - bash

I'm trying to write a script that allows you to enter your machine name, and then lets you know if the host is on the local network. Here's what I have:
#!/bin/bash
echo "Please enter the host you would like to ping:"
read -r host
output=$(ruptime | awk '{print $1}')
if [ "$output" == "$host" ];
then
echo "$host is up"
else
echo "$host is down"
fi
This works when I enter my machine name 'ubuntu' since I am the only one on my LAN and the awk statement outputs 'ubuntu'.
If I run for example:
#!/bin/bash
echo "Please enter the host you would like to ping:"
read -r host
output=$(cat /etc/hosts | awk '{print $1}')
if [ "$output" == "$host" ];
then
echo "$host is up"
else
echo "$host is down"
fi
The output is 2 lines: localhost and ubuntu. If I then run the script and enter either one of those, it says it's not found.
I think the awk is only looking for the value in the first line. How can I have the script check every line from the output of the awk and then compare it to what was entered?
Thanks in advance!

You're setting $output to all the names. You're not checking if $host is one of them, you're checking if $host is equal to all of them at once.
grep is a better way to do this.
if ruptime | grep -q -w "$host"
then echo "$host is up"
else echo "$host is down"
fi

Assuming that your goal is to look at whether a given name is in the first column of the output from ruptime, that might look like:
#!/usr/bin/env bash
hostname="target"
while read -r hostname _; do
[[ $hostname = "$target" ]] && { echo "$target is up"; break; }
done < <(ruptime)
read -r hostname _ puts only the first column of each line into hostname, putting remaining text into _.

Related

How to execute nmap from ping script

I'm learning bash for pentesting, so i started with kinda usefull things for me. I created a script, thath scan whole network by my requirements. But at the end of script i want to do Nmap of this Ips.
And thats the problem, no idea how to execute it.
time=$(date +"%T")
ip=(192.168.0)
echo -e "Scan started at $time"
for x in $(seq 1 254); do
ping -c 1 $ip.$x | grep "from" &
done
echo -n "Would you like to do NMAP scan (y/n)? "
read nmap
if [ "$nmap" != "${nmap#[Yy]}" ] ;then
#here i guess have to be something
else
#here i guess have to be something
fi
If user chose "y" than NMAP should scan IPs, which are pinged as live.
Here is a simple solution on how to use ping and nmap together
#!/bin/bash
ipfile=$(mktemp) # create tmpfile for online ip's
trap 'rm -f "$ipfile"' EXIT # rm tmpfile on exit
BASE="192.168.0" # define your IP base
for (( i=1; i <= 254; i++ )); do
ip="$BASE.$i"
printf "Scanning %s " "$ip"
# check if ip is online
if ping -c 1 "$ip" | grep "from" >/dev/null; then
printf "online\n"
# write ip's into tmpfile
printf "%s\n" "$ip" >> "$ipfile"
else
printf "offline\n"
fi
done
echo -n "Would you like to do NMAP scan (y/n)? "
read -r answer
if [ "$answer" = "y" ]; then
# check all ip's in tmpfile with nmap
nmap -iL "$ipfile"
fi

how can I loop with an error message?

Latest Version
#!/bin/bash
set -e
shopt -s nocasematch
#vars
redbgbold='\e[1;97;41m'
resetcolor='\e[0m'
RegExFQDN='(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.)+[a-zA-Z]{2,63}$)'
#functions
ask() {
local input
until
read -rp "$1 > " input >&2 || return 1
grep -q -P "$2" <<< "$input"
do
printf "ERROR - "${redbgbold}"\"$input\""${resetcolor}" is not a valid " >&2; sed "s/.*the //" <<< "$1" >&2
done
printf '%s\n' "$input"
}
#code
while [ -z $fqdn ]; do
fqdn=$(ask "Enter the FQDN" $RegExFQDN)
echo "FQDN is $fqdn"
done
The Question
I have a read line, and I want to take what the user entered and see if it matches my regex, if it matches we leave the loop, if it fails it prints an error and we do the loop again until we get a match. It looks redundant to me, and I assume there should be a better way but not sure what that should be.
Original Code
#!/bin/bash
set -e
shopt -s nocasematch
function RegexValidation() {
if [ "$2" = "fqdn" ]; then
if [ `echo $1 | grep -c -P '(?=^.{1,254}$)(^(?>(?!\d+\.)[a-z0-9_\-]{1,63}\.?)+(?:[a-z]{2,})$)'` == "0" ]; then
echo "ERROR - $1 is not a valid FQDN"
unset $!{1}
fi
fi
}
while [ -z $fqdn ]; do
read -e -r -p "Enter the Fully Qualified Domain Name > " fqdn
RegexValidation $fqdn fqdn
done
shopt -u nocasematch
any help is appreciated.
Update #1 - fixed formatting issues.
Update #2 - using that other guy's suggestions with a few additional tweaks
I would do basically the same thing, but split it differently to make it easier to reuse:
#!/bin/bash
set -e
ask() {
local input
until
read -rp "$1 > " input >&2 || return 1
grep -q -P "$2" <<< "$input"
do
echo "Invalid answer. Try again" >&2
done
printf '%s\n' "$input"
}
ask_fqdn() {
ask "$1" '(?=^.{1,254}$)(^(?>(?!\d+\.)[a-z0-9_\-]{1,63}\.?)+(?:[a-z]{2,})$)'
}
fqdn=$(ask_fqdn "Enter first FQDN")
echo "You wrote $fqdn"
fqdn=$(ask_fqdn "Enter second FQDN")
echo "This time it was $fqdn"
number=$(ask "And now a number because why not" '^\d+$')
echo "So $number"
Now you don't have to write a new loop every time you want new information, and you can easily ask for new things without modifying the existing functions.
Have the function return a status, which you can test with if in the loop.
And rather than use test to check the result of grep, just test it directly with if. grep returns a non-zero status if the input doesn't match.
function RegexValidation() {
if [ "$2" = "fqdn" ]; then
if ! echo "$1" | grep -q -P '(?=^.{1,254}$)(^(?>(?!\d+\.)[a-z0-9_\-]{1,63}\.?)+(?:[a-z]{2,})$)'; then
echo "ERROR - $1 is not a valid FQDN"
return 1
fi
return 0
fi
}
while :; do
read -e -r -p "Enter the Fully Qualified Domain Name > " fqdn
if RegexValidation "$fqdn" fqdn
then break
fi
done
Also, remember to quote your variables.

Nested if statement inside a for loop in bash script

I'm writing a bash script that goes through a for loop which is a list of each hostname, then will test each one if it's responding on port 22, if it is then execute an ssh session, however both the first and second if statements are only executed on the first host in the list, not the rest of the hosts. If the host isn't responding on port 22, I want the script to continue to the next host. Any ideas how to ensure the script runs the ssh on each host in the list? Should this be another for loop?
#!/bin/bash
hostlist=$(cat '/local/bin/bondcheck/hostlist_test.txt')
for host in $hostlist; do
test=$(nmap $host -P0 -p 22 | egrep 'open|closed|filtered' | awk '{print $2}')
if [[ $test = 'open' ]]; then
cd /local/bin/bondcheck/
mv active.current active.fixed
ssh -n $host echo -n "$host: ; cat /proc/net/bonding/bond0 | grep Active" >> active.current
result=$(comm -13 active.fixed active.current)
if [ "$result" == "" ]; then
exit 0
else
echo "$result" | cat -n
fi
else
echo "$host is not responding"
fi
done
exit 0 exits the entire script; you just want to move on to the next iteration of the loop. Use continue instead.
You problem is most likely in the lines
if [ "$result" == "" ]
then
exit 0
else
echo "$result" | cat -n
fi
Here the exit 0 causes the entire script to exit when the $result is empty. You could the way around using :
if [ "$result" != "" ] #proceeding on non-empty 'result'
then
echo "$result" | cat -n
fi

Bash comparisons with blank variable in simple nmap script

I have a simple script that nmaps from a list of ip's, and returns any ips that have port 80 open. The problem is when 80 is closed, instead of no return, Im getting "=: command not found" as it is attempting to compare a blank/empty variable.
#!/bin/bash
for i in$(cat filename.txt); do
str=$(nmap $i |grep 80 | cut -d ' ' -f2)
if($str = "open"); then
echo port 80 on $i is open
fi
done
When port 80 is closed, $str is getting nothing for a value, then when I try to compare nothing to the string "open", im getting "=: command not found". How do I check if $str has a value before comparing it to a string? I tried
if (! -z $str); then
which I thought meant
if $str is not null, then,
but could not get it to work properly. I certainly believe I understand why its is behaving as it is, I just dont know how to correct it.
With GNU bash:
#!/bin/bash
port="80"
while read -r host; do
str=$(nmap "$host" -p "$port" | grep "^$port")
ret="${PIPESTATUS[0]}" # returncode of nmap
if [[ $ret != 0 ]]; then
echo "error"
exit 1
fi
# check if $str contains "open"
if [[ $str =~ open ]]; then
echo "port $port on $host is open"
else
echo "port $port on $host is not open"
fi
done <filename.txt
I assume filename.txt contains only one IP or hostname per line.
Output (example):
port 80 on 10.20.30.40 is not open
port 80 on localhost is open
Here's a version using netcat's exit status:
#!/bin/bash
while read host; do
nc -zw2 $host 80 &>/dev/null && state=open || state=closed
echo "port 80 on $host is $state"
done < filename.txt
Thank you all for the help, Cyrus I like yours a lot, better than what I came up with, and Cole I was very happy to see it done with netcat. Here is what I eventually arrived at
#!/bin/bash
for i in$(cat targetlist.txt); do --multiple people say this doesnt work
str=$(nmap -p 80 $i | grep 80 | cut -d ' ' -f2) --im using bash 4.2.37(1)
if [[ ! -z $str && $str = "open" ]] ; then
echo port 80 on $i is open
fi
done

Return value of ping [duplicate]

This question already has an answer here:
checking if grep command returns correct output
(1 answer)
Closed 7 years ago.
I have done a file named hosts.txt which includes some websites, to test the ping on each website with a script. What I want to do with my script is I want to loop through each line that includes different websites, and it should tell if the website is up or down (by measuring the ping command on each)
What my problem is that I don't really know how to get the return value of the ping command, so in case a website is up it should say "'website name' found" or not found. I have been researching, also tried out the ! command and different ways in the if-statement, but none of them seem to work.
My code:
#!/bin/bash
echo
echo "Monitoring hosts from file hosts.txt ..."
echo
echo
egrep -v '^(#|$)' hosts.txt | while read line; do #put the egrep value
#which is the lines in hosts.txt, and loop through each one of them
if [ ping $line ];then
echo "$line is up"
else
echo "$line is not up"
fi
done
echo
You need to use the $? special variable.
For example:
ping $line
pingResponse = $?
if [ $pingResponse -eq 0 ];then
echo "$line is up"
else
echo "$line is not up"
fi
you can test for boolean like:
[[ `ping $line` ]]
.
#!/bin/bash
echo
echo "Monitoring hosts from file hosts.txt ..."
echo
echo
egrep -v '^(#|$)' hosts.txt | while read line; do #put the egrep value
#which is the lines in hosts.txt, and loop through each one of them
if [[ `ping $line` ]];then
echo "$line is up"
else
echo "$line is not up"
fi
done

Resources