per my last question, I managed to increment a given IPv6 Address by 1 to have the next possible address - hooray it works :)
But only until I hit the :ffff IP - the next would obviously be :1:0000 but my script is not "behaving" in a sane way - and I cannot figure out why :(
#!/bin/bash
initialipv6address="2001:0000:0000:0000:0000:0000:0000:ffff"
echo $initialipv6address
function ipv6_increment {
IFS=':' read -r -a fields <<< "$initialipv6address"
carry=1
for ((i=${#fields[#]}-1; i>=0; i--)); do
fields[$i]=$((16#${fields[i]}+carry))
if [[ ${fields[$i]} -ge 65536 ]]; then
carry=1
fields[$i]=$((16#${fields[i]}-65536))
else
carry=0
fi
done
printf "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n" "${fields[#]}"
}
newipv6address=$(ipv6_increment "$initialipv6address")
echo "$newipv6address"
Output expected:
2001:0000:0000:0000:0000:0000:0000:ffff
2001:0000:0000:0000:0000:0000:0001:0000
Output is in reality:
2001:0000:0000:0000:0000:0000:0000:ffff
2001:0000:0000:0000:0000:0000:0001:55536 <-- WTF?????
It seems like it correctly sets the value to 1 but I cannot figure out where the 55536 is coming from :(
Related
I have a scripts that needs to change the number each time is run. There can be max 3 digits from 0 to 999, it needs to repeat after it reaches 999.
#!/bin/sh
#Ncat is a part of the nmap, install nmap to use ncat
#Define variables
i=poland.aprs2.net
port=14580
lat=3439.94N
lon=02517.67E_ #_ is a symbol for WX station
user=APRS
password=99999
#Generate authentication data
aprsauth="user $user pass $password filter m/50"
#Generate Weather data
xastir="$user>APX206,TCPIP*:!$lat/$lon".../...g...t...h41X210""
#Telemetry data
t1="$user>APX206,TCPIP*:T#672,060,000,000,000,000,00000000"
#Send data to the APRS server
printf "%s\n" "$aprsauth" "$xastir" "$t1"| ncat --send-only $i $port
#Output control
printf "%s\n" "$aprsauth" "$xastir" "$t1"
Code that needs to be changed is T#672
from:
t1="$user>APX206,TCPIP*:T#672,060,000,000,000,000,00000000"
T#xxx is a sequence number in APRS protocol, and it only supports 3 digits. Each telemetry report needs a different sequence number.
You need to maintain some sort of external state: write the last number used to a file, and read from that file on startup so you can incremented t it.
read num < /var/run/myscript/last_value
num=$((num + 1))
if (( num == 1000 )); then
num=0
fi
echo "$num" > /var/run/myscript/last_value
...
printf -v t1 "%s>APX206,TCPIP*:T#%03d,060,000,000,000,000,00000000" "$user" "$num"
My goal is to take a list of IPs, sometimes the list is just a single IP or it could be a range of IPs and count how many IPs there are in a list. We have another product that generates a list of IPs and we have been manually counting it using an Excel spreadsheet.
Using an existing post here on Stack, I have been attempting to incorporate it into a script that will accept a list. https://codegolf.stackexchange.com/questions/28760/how-many-ip-addresses-are-in-a-given-range (See Pure Bash, 66 bytes).
Instead of grabbing two arguments on the command line $1 and $2, I pull a list into an array then enumerate through the array. If it is a single IP, it just adds 1 to the counter variable, if it is a range it uses the logic to convert the IPs to hex and counts them. I'm running into an issue where I am receiving errors.
I can't seem to figure out why this says "invalid number: printf: 229". I've read up on the expansion principle and I cannot seem to get my head around why it keeps throwing this error and calculating it improperly.
I've used this site for years, this is my first post. Any help would be greatly appreciated!
Thank you!
This is what I have so far:
#!/bin/bash
if [ $# -lt 1 ]; then
echo "Please supply a list of IP addresses"
echo "Example: " $0 "list.txt"
exit
fi
#Set some variables
LIST=($(cat ./$1))
COUNT=0
# Function for removing dots and converting to hex
p()(printf %02x ${1//./ })
# Enumerate the array of IPs
for RANGE in ${LIST[#]};do
IFS=- read IP1 IP2 <<< $RANGE
if [ -z $IP2 ]; then
COUNT=$[COUNT + 1]
else
r=$[0x`p $IP1`-0x`p $IP2`]
COUNT=$[COUNT + $[1+${r/-}]]
fi
done
echo "The count is" $COUNT
sample_range.txt:
192.168.47.11
192.168.48.10
192.168.65.228-192.168.65.229
192.168.65.234
192.168.65.239
192.168.65.241
192.168.65.244
192.168.80.220
192.168.93.231-192.168.93.235
192.168.93.237-192.168.93.239
192.168.103.222
This should result in 18, instead it gives me this output:
# ./rc.sh sample_range.txt
: invalid number: printf: 229
: invalid number: printf: 235
: invalid number: printf: 239
The count is 707
IPs are numbers base 256.
#!/bin/bash
ipdiff() {
declare -i dec1 dec2 diff # set integer attribute
dec1=$1*256*256*256+$2*256*256+$3*256+$4
dec2=$5*256*256*256+$6*256*256+$7*256+$8
diff=$dec2-$dec1+1
counter=counter+$diff
}
declare -i counter
# read IP(s) from file, use . and - as separator
while IFS=".-" read -r a1 a2 a3 a4 b1 b2 b3 b4; do
if [[ -z $b1 ]]; then # $b1 is empty (line with one IP)
counter=counter+1
else # $b1 is not empty (line with 2 IPs)
ipdiff $a1 $a2 $a3 $a4 $b1 $b2 $b3 $b4
fi
done < file
echo $counter
Output:
18
I am trying to write a BASH script that downloads some transcripts of a podcast with cURL. All transcript files have a name that only differs by three digits: filename[three-digits].txt
from filename001.txt
to.... filename440.txt.
I store the three digits as a number in a variable and increment the variable in a while loop. How can I increment the number without it losing its leading zeroes?
#!/bin/bash
clear
# [...] code for handling storage
episode=001
last=440
secnow_transcript_url="https://www.grc.com/sn/sn-"
last_token=".txt"
while [ $episode -le $last ]; do
curl -X GET $secnow_transcript_url$episode$last_token > # storage location
episode=$[$episode+001];
sleep 60 # Don't stress the server too much!
done
I searched a lot and discovered nice approaches of others, that do solve my problem, but out of curiosity I would love to know if there is solution to my problem that keeps the while-loop, despite a for-loop would be more appropriate in the first place, as I know the range, but the day will come, when I will need a while loop! :-)
#!/bin/bash
for episode in $(seq -w 01 05); do
curl -X GET $secnow_transcript_url$episode$last_token > # ...
done
or for just a few digits (becomes unpractical for more digits)
#!/bin/bash
for episode in 00{1..9} 0{10..99} {100..440}; do
curl -X GET $secnow_transcript_url$episode$last_token > # ...
done
You can use $((10#$n)) to remove zero padding (and do calculations), and printf to add zero padding back. Here are both put together to increment a zero padded number in a while loop:
n="0000123"
digits=${#n} # number of digits, here calculated from the original number
while sleep 1
do
n=$(printf "%0${digits}d\n" "$((10#$n + 1))")
echo "$n"
done
for ep in {001..440} should work.
But, as you want a while loop: let printf handle leading zeroes
while (( episode <= last )); do
printf -v url "%s%03d%s" $secnow_transcript_url $episode $last_token
curl -X GET $url > # storage location
(( episode++ ))
sleep 60 # Don't stress the server too much!
done
Will this do?
#!/bin/bash
i=1
zi=000000000$i
s=${zi:(-3)}
echo $s
I'm having an issue when i try to port my bash script to nagios.The scripts works fine when I run on console, but when I run it from Nagios i get the msg "(null)" - In the nagios debug log I see that it parse the script well but it returns the error msg..
I'm not very good at scripting so i guess i'll need some help
The objective of the script is to check *.ears version from some servers, md5 them and compare the output to see if the version matches or not.
To do that, i have a json on these servers that prints the name of the *.ear and his md5.
so.. The first part of the script gets that info from the json with curl and stores just the md5 number on a .tempfile , then it compares both temp files and if they match i got the $STATE_OK msg. If they dont , it creates a .datetmp file with the date ( the objective of this is to print a message after 48hs of inconsistence). Then, i make a diff of the .datetmp file and the days i wanna check if the result is less than 48hrs it prints the $STATE_WAR, if the result is more than 48 hrs it Prints the $STATE_CRI
The sintaxis of the script is " $ sh script.sh nameoftheear.ear server1 server2 "
Thanks in advance
#/bin/bash
#Variables For Nagios
cont=$1
bas1=$2
bas2=$3
## Here you set the servers hostname
svr1= curl -s "http://$bas1.domain.com:7877/apps.json" | grep -Po '"EAR File":.*? [^\\]",' | grep $cont | awk '{ print $5 }' > .$cont-tmpsvr1
svr2= curl -s "http://$bas2.domain.com:7877/apps.json" | grep -Po '"EAR File":.*? [^\\]",' | grep $cont | awk '{ print $5 }' > .$cont-tmpsvr2
file1=.$cont-tmpsvr1
file2=.$cont-tmpsvr2
md51=$(head -n 1 .$cont-tmpsvr1)
md52=$(head -n 1 .$cont-tmpsvr2)
datenow=$(date +%s)
#Error Msg
ERR_WAR="Not updated $bas1: $cont $md51 --- $bas2: $cont $md52 "
ERR_CRI="48 hs un-updated $bas1: $cont $md51 --- $bas2: $cont $md52 "
OK_MSG="Is up to date $bas1: $cont $md51 --- $bas2: $cont $md52 "
STATE_OK=0
STATE_WARNING=1
STATE_CRITICAL=2
##Matching md5 Files
if cmp -s "$file1" "$file2"
then
echo $STATE_OK
echo $OK_MSG
# I do the rm to delete the date tmp file so i can get the $STATE_OK or $STATE_WARNING
rm .$cont-datetmp
exit 0
elif
echo $datenow >> .$cont-datetmp
#Vars to set modification date
datetmp=$(head -n 1 .$cont-datetmp)
diffdate=$(( ($datenow - $datetmp) /60 ))
#This var is to set the time of the critical ERR
days=$((48*60))
[ $diffdate -lt $days ]
then
echo $STATE_WARNING
echo $ERR_WAR
exit 1
else
echo $STATE_CRITICAL
echo $ERR_CRI
exit 2
fi
I am guessing some kind of permission problem - more specifically I don't think the nagios user can write to it's own home directory. You either fix those permissions or write to a file in /tmp (and consider using mktemp?).
...but ideally you'd skip writing all those files, as far as I can see all of those comparisons etc could be kept in memory.
UPDATE
Looked at your script again - I see some obvious errors you can look into:
You are printing out the exit value before you print the message.
You print the exit value rather than exit with the exit value.
...so this:
echo $STATE_WARNING
echo $ERR_WAR
exit 1
Should rather be:
echo $ERR_WAR
exit $STATE_WARNING
Also I am wondering if this is really the script or if you missed something when pasting. There seems to be missing an 'if' and also a superfluous line break in your last piece of code? Should rather be:
if [ $diffdate -lt $days ]
then
...
else
...
fi
I have the following bash script which I will use to analyze all report files in the current directory:
#!/bin/bash
# methods
analyzeStructuralErrors()
{
# do something with $1
}
# main
reportFiles=`find $PWD -name "*_report*.txt"`;
for f in $reportFiles
do
echo "Processing $f"
analyzeStructuralErrors $f
done
My report files are formatted as such:
Error Code for Issue X - Description Text - Number of errors.
col1_name,col2_name,col3_name,col4_name,col5_name,col6_name
1143-1-1411-247-1-72953-1
1143-2-1411-247-436-72953-1
2211-1-1888-204-442-22222-1
Error Code for Issue Y - Description Text - Number of errors.
col1_name,col2_name,col3_name,col4_name,col5_name,col6_name
Other data
.
.
.
I'm looking for a way to go through each file and aggregate the report data. In the above example, we have two unique issues of type X, which I would like to handle in analyzeStructural. Other types of issues can be ignored in this routine. Can anyone offer advice on how to do this? I want to read each line until I hit the next error basically, and put that data into some kind of data structure.
Below is a working awk implementation that uses it's pseudo multidimensional arrays. I've included sample output to show you how it looks. I took the liberty to add a 'Count' column to denote how many times a certain "Issue" was hit for a given Error Code
#!/bin/bash
awk '
/Error Code for Issue/ {
errCode[currCode=$5]=$5
}
/^ +[0-9-]+$/ {
split($0, tmpArr, "-")
error[errCode[currCode],tmpArr[1]]++
}
END {
for (code in errCode) {
printf("Error Code: %s\n", code)
for (item in error) {
split(item, subscr, SUBSEP)
if (subscr[1] == code) {
printf("\tIssue: %s\tCount: %s\n", subscr[2], error[item])
}
}
}
}
' *_report*.txt
Output
$ ./report.awk
Error Code: B
Issue: 1212 Count: 3
Error Code: X
Issue: 2211 Count: 1
Issue: 1143 Count: 2
Error Code: Y
Issue: 2961 Count: 1
Issue: 6666 Count: 1
Issue: 5555 Count: 2
Issue: 5911 Count: 1
Issue: 4949 Count: 1
Error Code: Z
Issue: 2222 Count: 1
Issue: 1111 Count: 1
Issue: 2323 Count: 2
Issue: 3333 Count: 1
Issue: 1212 Count: 1
As suggested by Dave Jarvis, awk will:
handle this better than bash
is fairly easy to learn
likely available wherever bash is available
I've never had to look farther than The AWK Manual.
It would make things easier if you used a consistent field separator for both the list of column names and the data. Perhaps you could do some pre-processing in a bash script using sed before feeding to awk. Anyway, take a look at multi-dimensional arrays and reading multiple lines in the manual.
Bash has one-dimensional arrays that are indexed by integers. Bash 4 adds associative arrays. That's it for data structures. AWK has one dimensional associative arrays and fakes its way through two dimensional arrays. If you need some kind of data structure more advanced than that, you'll need to use Python, for example, or some other language.
That said, here's a rough outline of how you might parse the data you've shown.
#!/bin/bash
# methods
analyzeStructuralErrors()
{
local f=$1
local Xpat="Error Code for Issue X"
local notXpat="Error Code for Issue [^X]"
while read -r line
do
if [[ $line =~ $Xpat ]]
then
flag=true
elif [[ $line =~ $notXpat ]]
then
flag=false
elif $flag && [[ $line =~ , ]]
then
# columns could be overwritten if there are more than one X section
IFS=, read -ra columns <<< "$line"
elif $flag && [[ $line =~ - ]]
then
issues+=(line)
else
echo "unrecognized data line"
echo "$line"
fi
done
for issue in ${issues[#]}
do
IFS=- read -ra array <<< "$line"
# do something with ${array[0]}, ${array[1]}, etc.
# or iterate
for field in ${array[#]}
do
# do something with $field
done
done
}
# main
find . -name "*_report*.txt" | while read -r f
do
echo "Processing $f"
analyzeStructuralErrors "$f"
done