Parse IP and Download-Total from mikrotik - regex

I wanna extract IP and download-total from mikrotik command /queue simple print stat
Here's some example :
0 name="101" target=192.168.10.101/32 rate=0bps/0bps total-rate=0bps
packet-rate=0/0 total-packet-rate=0 queued-bytes=0/0
total-queued-bytes=0 queued-packets=0/0 total-queued-packets=0
bytes=17574842/389197663 total-bytes=0 packets=191226/308561
total-packets=0 dropped=9/5899 total-dropped=0
1 name="102" target=192.168.10.102/32 rate=0bps/0bps total-rate=0bps
packet-rate=0/0 total-packet-rate=0 queued-bytes=0/0
total-queued-bytes=0 queued-packets=0/0 total-queued-packets=0
bytes=65593392/183786457 total-bytes=0 packets=163260/166022
total-packets=0 dropped=175/2403 total-dropped=0
2 name="103" target=192.168.10.103/32 rate=0bps/0bps total-rate=0bps
packet-rate=0/0 total-packet-rate=0 queued-bytes=0/0
total-queued-bytes=0 queued-packets=0/0 total-queued-packets=0
bytes=3263234/67407044 total-bytes=0 packets=41437/52602
total-packets=0 dropped=0/546 total-dropped=0
All that I need is :
192.168.10.101 389197663
192.168.10.102 183786457
192.168.10.103 67407044
But I get
target=192.168.10.101/32
bytes=17574842/389197663
target=192.168.10.102/32
bytes=65593392/183786457
target=192.168.10.103/32
bytes=3263234/67407044
I try it with grep -oP 'target=.*?\ |[^\-]bytes=.*?\ ' | sed 's/^ //g'.
So, how can I parse it? Sorry for bad english..

Just continue your line of parsing with another pipes (most easy way i think)
grep -oP 'target=.*?\ |[^\-]bytes=.*?\ ' file | sed 's/^ //g' | sed -r 's/target=([^/]*)[/].*/\1/; s/bytes=[^/]*[/]//' | sed 'N; s/\n/ /'
output
192.168.10.101 389197663
192.168.10.102 183786457
192.168.10.103 67407044

sed '/^[0-9]\{1,\}[[:blank:]]\{1,\}name/,/^[[:blank:]]*$/ {
/^[0-9]/{
s#.*target=\([^/]*\).*#\1#;h;d
}
\#^[[:blank:]]*bytes=[0-9]*/\([0-9]*\).*# !d
s//\1/
G
s/\(.*\)\n\(.*\)/\2 \1/p
}
d
' YourFile
A bit long but do the job in 1 sed
awk '{
if ( $3 ~ /target=/ ) split( $3, aIP, "[=/]")
if ( $1 ~ /^[[:blank:]]*bytes=[0-9]*/ ) {
split( $1, aByt, "/")
print aIP[2] " " aByt[2]
}
}' YourFile
same in awk
if always same exact structure
awk 'BEGIN{ RS="" }
{ split( $3, aIP, "[=/]"); split( $12, aByt, "/")
print aIP[2] " " aByt[2]
}' YourFile

Related

How to replace pipe delimited to white space in a specific range of columns?

I have use awk and sed to replace pipe with white space, here's my code:
awk -F "|" -v OFS=" " ' $1=$1 '
sed "s/|/ /g" try.log
But it deletes all the pipe in my data. Here's a sample data:
JAP|09|7777|TECHNOLOGY|AGRICULTURE|INDUSTRY
The result I want is this:
JAP 09 7777|TECHNOLOGY|AGRICULTURE|INDUSTRY
Thanks in advance.
Perl solution:
perl -pe '$c = 0; s/\|/ /, $c++ while $c < 2'
This might work for you (GNU sed):
sed 's/|/ /;s// /' file
A programmatic solution might be:
sed 'y/|/\n/;s/\n/|/3g;y/\n/ /' file
Try this one liner awk:
awk -F"|" '{a=""; for (i=1;i<=NF; i++)if(i <=3) a=a " "$i; else a=a "|"$i; sub("^[ ]","",a); print a}'
Long format:
BEGIN {
FS="|";
}
{
a="";
for (i=1; i<= NF; i++)
{
if (i <= 3)
{
a=a " "$i;
}
else
{
a=a "|"$i;
}
}
sub("^[ ]","",a);
print a;
}
Output:
echo "JAP|09|7777|TECHNOLOGY|AGRICULTURE|INDUSTRY" | awk -f script.awk
JAP 09 7777|TECHNOLOGY|AGRICULTURE|INDUSTRY
awk '{sub(/\|09\|/," 09 ")}1' file
JAP 09 7777|TECHNOLOGY|AGRICULTURE|INDUSTRY

Awk BEGIN example

$ cat tables.txt | awk 'BEGIN {
RS="\nStation"
FS="\n"
}
{ print $1 }
'
Running the above command in the above format or as a script gives me the desired output.
08594: SAL , CAPE VERDE
But if I try running the same in CLI as a single gives me error as syntax. What I am doing wrong here?
$ awk 'BEGIN { RS="\nStation" FS="\n" }{ print $1 }' tables.txt
You can use:
awk 'BEGIN { RS="\nStation"; FS="\n" }{ print $1 }' tables.txt
i.e. use ; to terminate one assignment before starting another i.e. FS="\n".

Tokenize and capture with sed

Suppose we have a string like
"dir1|file1|dir2|file2"
and would like to turn it into
"-f dir1/file1 -f dir2/file2"
Is there an elegant way to do this with sed or awk for a general case of n > 2?
My attempt was to try
echo "dir1|file1|dir2|file2" | sed 's/\(\([^|]\)|\)*/-f \2\/\4 -f \6\/\8/'
An awk solution:
awk -F'|' '{ for (i=1;i<=NF;i+=2) printf "-f %s/%s%s", $i, $(i+1), ((i==NF-1) ? "\n" : " ") }' \
<<<"dir1|file1|dir2|file2"
-F'|' splits the input into fields by |
for (i=1;i<=NF;i+=2) loops over the field indices in increments of 2
printf "-f %s/%s%s", $i, $(i+1), ((i==NF-1) ? "\n" : " ") prints pairs of consecutive fields joined with / and prefixed with -f<space>
((i==NF-1) ? "\n" : " ") terminates each field-pair either with a space, if more fields follow, or a \n to terminate the overall output.
In a comment, the OP suggests a shorter variation, which may be of interest if you don't need/want the output to be \n-terminated:
awk -F'|' '{ for (i=1;i<=NF;++i) printf "%s", (i%2 ? " -f " $i : "/" $i ) }' \
<<<"dir1|file1|dir2|file2"
This might work for you (GNU sed):
sed 's/\([^|]*\)|\([^|]*\)|\?/-f \1\/\2 /g;s/ $//' file
This will work for dir1|file1|dir2|file2|dirn|filen type strings
The regexp forms two back references (\1,\2 used in the replacement part of the substitution command s/pattern/replacement/), the first is all non-|'s, then a |, the second is all non-|'s then an optional | i.e. for the first application of the substitution (N.B. the g flag is implemented and so the substitutions may be multiple) dir1 becomes \1 and file1 becomes \2. All that remains is to prepend -f and replace the first | by / and the second | by a space. The last space is not needed at the end of the line and is removed in the second substitution command.
$ awk -v RS='|' 'NR%2{p=$0;next} {printf " -f %s/%s", p, $0}' <<< 'dir1|file1|dir2|file2'
-f dir1/file1 -f dir2/file2
A gnu-awk solution:
s="dir1|file1|dir2|file2"
awk 'BEGIN{ FPAT="[^|]+\\|[^|]+" } {
for (i=1; i<=NF; i++) {
sub(/\|/, "/", $i);
if (i>1)
printf " ";
printf "-f " $i
};
print ""
}' <<< "$s"
-f dir1/file1 -f dir2/file2
FPAT is used for grabbing dir1|file2 into single field.

Bash command to match n line

I have an index HTML file with file/dir listing. It is just a usual filebrowser like :
...content here...
<td>20120011/</td>
<td>20120111/</td>
<td>20120211/</td>
<td>20120411/</td>
...content here...
I don't understand how to extract the 2nd line from the bottom.
1) I downloaded HTML with curl
content=$(curl -sL "http://path-to-html")
2) then used
dir=$(echo $content | sed '/.*href="\([0-9]*\/\)".*/!d;s//\1/;q')
which gives me the last match : 20120411.
But how to get the previous one ?
I don't know the total count of items.
This awk program will print the penultimate line:
echo ${content} | awk '{ pen = ult; ult = $0 } END { print pen }'
This will print the penultimate matching line:
echo ${content} | awk '/href="([0-9]{8}\/)"/ { pen = ult; ult = $0 } END { print pen }'
If you just want to extract the first capture group:
echo ${content} | awk 'match($0, /href="([0-9]{8}\/)"/, a) { pen = ult; ult = a[1] } END { print pen }'
Putting it all together:
bash-4.2$ dir=$(curl -sL http://www.arteetmarte.no/tmp/index.html |
awk 'match($0, /href="([0-9]{8}\/)"/, a) {
pen = ult
ult = a[1]
}
END {
print pen
}
')
bash-4.2$ echo ${dir}
20130918/
Tested with: GNU Awk 4.1.0, API: 1.0
May be a bit easier with awk
dir=$(echo "$content"|awk '/href=/{x=p;p=$0}END{sub(/.*">/,"",x);sub(/<.*/, "",x); print x}')
dir=$(echo $content | sed sed -n '/href="\([0-9]\{1,\}\/\)"/ {s|.*href="\([0-9]\{1,\}/\)".*|-\1-|;H;}
$ {x;l;s|.*-\([0-9]\{1,\}/\)-\(\n-[0-9]\{1,\}/-\)\{1\}$|\1|p;}')
The 1 in \{1\}$ specify how much line must be removed from the end

Awk gensub transformation

echo "0.123e2" | gawk '{print gensub(/([0-9]+\.[0-9]+)e([0-9]+)/, "\\1 * 10 ^ \\2", "g")}'
gives me "0.123 * 10 ^ 2" as a result as expected.
Is there a way to actually tell it to calculate the term to "12.3" ?
In general: Is there a way to modify/transform the matches (\\1,\\2,...)?
It could be easier with perl:
perl -pe 's/(\d+\.\d+e\d+)/ sprintf("%.1f",$1) /ge' filename
With your test data:
echo '0.123e2 xyz/$&" 0.3322e12)282 abc' | perl -pe 's/(\d+\.\d+e\d+)/ sprintf("%.1f",$1) /ge'
12.3 xyz/$&" 332200000000.0)282 abc
With awk:
awk '{
while ( match( $0, /[0-9]+\.[0-9]+e[0-9]+/ ) > 0 ) {
num = sprintf("%.1f", substr( $0, RSTART, RLENGTH ) )
sub( /[0-9]+\.[0-9]+e[0-9]+/, num )
}
print $0
}' filename
You just want to use printf to specify the output format:
$ echo "0.123e2" | awk '{printf "%.1f\n",$0}'
12.3