How Do I Separate a Line of Bash Script Over Multiple Lines? - linux

So I've created this ping sweep in a bash terminal, but I want to make a neat looking script file for this:
for IPs in 192.168.0.{1..254}; do ping -c1 -W1 $IPs; done | grep -B1 "1 received" | grep "192.168.0" | cut -d " " -f2 > BashPingSweep.txt
I think I have the for loop correct, but I cant pipe the for loop into the other greps and cut then output. This is what I have now:
#!/bin/bash
for IPs in 192.168.0.{1..254}
do
ping -c1 -W1 $IPs
done
grep -B1 "1 received"
grep "192.168.0"
cut -d " " -f2
> BashPingSweep.txt

You could try this:
#!/bin/bash
for IP in 192.168.0.{1..254}
do
ping -c1 -W1 $IP
done |
grep -B1 "1 received" |
grep "192.168.0" |
cut -d " " -f2 \
> BashPingSweep.txt
It looks a bit awkward but it's a common way to format a lengthy pipe over multiple lines. You could also put it like this:
for IP in 192.168.0.{1..254}
do
ping -c1 -W1 $IP
done \
| grep -B1 "1 received" \
| grep "192.168.0" \
| cut -d " " -f2 \
> BashPingSweep.txt
which is what I prefer because it's easier to see where the pipe goes.

You need to pipe it inside the for loop
for IPs in 192.168.0.{1..254};
do
ping -c1 -W1 $IPs | grep -B1 "1 .* received" | grep "192.168.0" | cut -d " " -f2 > BashPingSweep.txt
done

Related

Problem with putting value in array in bash

I would like to make array which put users in a time using for loop. For example:
y[1]="user1"
y[2]="user2"
...
y[n]="usern"
I tried to do it like this
#!/bin/bash
x=$(who | cut -d " " -f1 | sort | uniq | wc -l)
for (( i=1; i<=$x; i++ )); do
y[$i]=$(who | cut -d " " -f1 | sort | uniq | sed -n '$ip')
p[$i]=$(lsof -u ${y[$i]} | wc -l)
echo "Users:"
echo ${y[$i]}
echo -e "Number of launched files:\n" ${p[$i]}
done
Most likely I'm using command "sed" wrong.
Can you help me?
Indeed your sed command seems to be a bit off. I can't really guess what you're trying to do there. Besides that, I'm wondering why you're executing who twice. You can make use of the data first obtained in the following manner.
#!/bin/bash
# define two arrays
y=()
p=()
#x=0
while read -r username; do
y+=("$username")
p+=($(lsof -u $(id -u "$username") | wc -l))
echo -e "User:\n${y[-1]}"
echo -e "Open files:\n${p[-1]}"
# The -1 index is the last index in the array, but you
# could uncomment the x=0 variable and the line below:
#((x++))
done <<< $(who | cut -d " " -f1 | sort | uniq)
echo "Amount of users: $x"
exit 0

Bash Command Substitution fail

I need some Specs and Data from Number of Latops in a ini-file.
When I try to get the IP-Adress with ip addr list <interface> nothing happens in the script. Not even a Error.
I try to quote stuff in my code. Nothing change.
for w2 in /sys/class/net/wl*
do
w2i=$(basename $w2)
echo $w2i
addr=$(ip -o -4 addr list $w2i | awk '{print $4}' | cut -d/ -f1)
echo $addr
echo -e "$w2i=$addr"
done
I Think maybe it is because of the Varibales but this fails also (interface is set 'manually'):
for w2 in /sys/class/net/wl*
do
w2i=$(basename $w2)
echo $w2i
addr=$(ip -o -4 addr list wlp3s0 | awk '{print $4}' | cut -d/ -f1)
echo $addr
echo -e "$w2i=$addr"
done
When i run the script i get wlp3s0 and i must close the script with ctl-c. When i run ip -o -4 addr list wlp3s0 | awk '{print $4}' | cut -d/ -f1 it gives me my ip like i expected.
EDIT
I just need to wirte the full-path of the ip-command. Can somebody explain why i have to do this? And especially it is not necessary for cut or awk that also based in /usr/bin/?
for w2 in /sys/class/net/wl*
do
w2i=$(basename $w2)
echo $w2i
addr=$(/usr/bin/ip -o -4 addr list "$w2i" | awk '{print $4}' | cut -d/ -f1)
echo $addr
echo -e "$w2i=$addr"
done
}

Unix - alter IP field an csv and output new file

Hoping for some input on this, as I'm struggling. I have a csv that contains an IP mask in which I want to get the network IP and broadcast IP.
So for instance I want the input field 1.0.0.0/24 to output 2 new field that contain the vaules: 1.0.0.0 in one and 1.0.0.255 in the other.
I have code to do this.
For broadcast:
for i in $(cat geoip.csv);do bcaddr=$(ipcalc -n -b $i);echo
${bcaddr#BROADCAST=};done
And for network:
for i in $(cat geoip.csv);do bcaddr=$(ipcalc -n -4 $i);echo
${bcaddr#BROADCAST=};done
Where do I go from here? How do I go about appending these 2 new fields to a new output file?
Thanks in advance!
see if this works on RHEL 5.x... do you want the output in csv with comma separator?
for i in $(cat csip.csv); do echo "$i $(ipcalc -n -b $i | grep -E "Address|Broadcast" | awk {'print ","$2'} | tr '\n' ' ')" ; done > new.csv
For your version of ipcalc, please try this:
for i in $(cat csip.csv); do echo "$i $(ipcalc -n -b $i | grep -E "NETWORK|BROADCAST" | awk -F= {'print ","$2'} | tr '\n' ' ')" ; done > new.csv
Why not using awk?
If you are using Linux (Not Unix):
for i in $(cat geoip.csv);do bcaddr=$(ipcalc -n -b $i); echo $bcaddr | egrep -o '([0-9]+\.){3}[0-9]+' ; done > ip.txt
Output:
1.0.0.255
1.0.0.0
If you want the output in a single line:
for i in $(cat geoip.csv);do bcaddr=$(ipcalc -n -b $i); echo $bcaddr | egrep -o '([0-9]+\.){3}[0-9]+' ORS=' ' ; done}' > ip.txt
Output:
1.0.0.255 1.0.0.0

extract average time from ping -c

I want to extract from the command ping -c 4 www.stackoverflow.com | tail -1| awk '{print $4}'
the average time.
107.921/108.929/110.394/0.905 ms
Output should be: 108.929
One way is to just add a cut to what you have there.
ping -c 4 www.stackoverflow.com | tail -1| awk '{print $4}' | cut -d '/' -f 2
ping -c 4 www.stackoverflow.com | tail -1| awk -F '/' '{print $5}' would work fine.
"-F" option is used to specify the field separator.
This might work for you:
ping -c 4 www.stackoverflow.com | sed '$!d;s|.*/\([0-9.]*\)/.*|\1|'
The following solution uses Bash only (requires Bash 3):
[[ $(ping -q -c 4 www.example.com) =~ \ =\ [^/]*/([0-9]+\.[0-9]+).*ms ]] \
&& echo ${BASH_REMATCH[1]}
For the regular expression it's easier to read (and handle) if it is stored in a variable:
regex='= [^/]*/([0-9]+\.[0-9]+).*ms'
[[ $(ping -q -c 4 www.example.com) =~ $regex ]] && echo ${BASH_REMATCH[1]}
Promoting luissquall's very elegent comment to an answer:
ping -c 4 www.stackoverflow.com | awk -F '/' 'END {print $5}'
Direct extract mean time from ping command:
ping -w 4 -q www.duckduckgo.com | cut -d "/" -s -f5
Options:
-w time out 4 seconds
-q quite mode
-d delimiter
-s skip line without delimiter
-f No. of field - depends on your system - sometimes 5th, sometimes 4th
I personly use is this way:
if [ $(ping -w 2 -q www.duckduckgo.com | cut -d "/" -s -f4 | cut -d "." -f1) -lt 20 ]; then
echo "good response time"
else
echo "bad response time"
fi
Use these to get current ping as a single number:
123.456:
ping -w1 -c1 8.8.8.8 | tail -1| cut -d '=' -f 2 | cut -d '/' -f 2
123:
ping -w1 -c1 8.8.8.8 | tail -1| cut -d '=' -f 2 | cut -d '/' -f 2 | cut -d '.' -f 1
Note that this displays the average of only 1 ping (-c1), you can increase the sample size by increasing this number (i.e. -c1337)
This avoids using awk (like #Buggabill posted), which doesn't play nice in bash aliases + takes a nanosecond longer
None of these worked well for me due to various issues such as when a timeout occurs. I only wanted to see bad ping times or timeouts and wanted PING to continue quickly, and none of these solutions worked. Here's my BASH script that works well to do both. Note that in the ping command, response time is limited to 1 second.
I realize this does not directly answer the OP's question, however it does provide a good way to deal with some issues that occur with some of the incomplete "solutions" provided here, thus going beyond the scope of the OPs question, which others coming here are looking for (I cite myself as an example), so I decided to share for those people, not specifically OP's question.
while true
do
###Set your IP amd max milliseconds###
ip="192.168.1.53"
maxms=50
###do not edit below###
err="100% packet loss"
out="$(ping -c 1 -i 1 -w 1 $ip)"
t="$(echo $out | awk -F '/' 'END {print $5}')"
t=${t%.*}
re='^[0-9]+$'
if ! [[ $t =~ $re ]] ; then
if [[ $out == *"$err"* ]] ; then
echo "`date` | ${ip}: TIMEOUT"
else
echo "error: Not a number: ${t} was found in: ${out}"
fi
else
if [ "$t" -gt $maxms ]; then
echo "`date` | ${ip}: ${t} ms"
fi
fi
done

extracting-the-values-using-grep

From extracting the values using grep
Reading:RG1:+ /user/reading-2/Monday:12
Reading:RG1:- /user/**/Friday:12
Reading:RG1:- /user/**/*.txt:12
Reading:RG1:- /user/tet-23/**/*.txt:12
Reading:RG2:+ /user/reading-2/Monday:12
Reading:RG2:- /user/**/Friday:12
Reading:RG2:- /user/**/*.txt:12
Reading:RG2:- /user/tet-23/**/*.txt:12
I have tried with this :
cat a.txt | grep RG1|grep '-'| cut -d':' -f3-| cut -d'-' -f2 |sed -e 's/ //'
This wont work because it will extract wrong path because some of + also having -
How to reslove this issue
Try this:
egrep "^[^:]*:RG1:-" a.txt | cut -d: -f3 | cut -b3-
Sample run:
$ cat a.txt
Reading:RG1:+ /user/reading-2/Monday:12
Reading:RG1:- /user/**/Friday:12
Reading:RG1:- /user/**/*.txt:12
Reading:RG1:- /user/tet-23/**/*.txt:12
Reading:RG2:- /user/tet-23/**/*.txt:12
Reading:RG2:+ /user/reading-2/Monday:12
$ egrep "^[^:]*:RG1:-" a.txt | cut -d: -f3 | cut -b3-
/user/**/Friday
/user/**/*.txt
/user/tet-23/**/*.txt
"^[^:]*:RG1:-" means "start with anything but : zero or more times, then a :, then a RG1, followed by -.
You may try to get the complete "RG1:+" string, and then cut by space, for example:
grep "RG1:+" a.txt | cut -d" " -f2
Try it with sed
sed -r -e '/:RG1:/s/.*:[+-] //;s/:[0-9]+$//' a.txt
Which will operate only on lines with :RG1: in them. You can generalize this for all lines:
sed -r -e 's/.*:[+-] //;s/:[0-9]+$//' a.txt
Or just lines with RG and a number
sed -r -e '/:RG[0-9]+:/s/.*:[+-] //;s/:[0-9]+$//' a.txt
If you want to keep the trailing :12 simply omit the final substitution, e.g.:
sed -r -e '/:RG[0-9]+:/s/.*:[+-] //' a.txt
If Perl is alowed
- or +
perl -nE 'say $1 if /\AReading:RG[1-9]:[+-]\s+(.*)\Z/' file
only -
perl -nE 'say $1 if /\AReading:RG[1-9]:-\s+(.*)\Z/' file
Try this variation on the answer I gave to your previous question:
grep -Po '(?<=RG1:- ).*(?=:\d*$)' a.txt

Resources