Printing a string value in awk results in error - awk

fatal: not enough arguments to satisfy format string
`%s SPT=80'
^ ran out for this one
This my code
for ((h = 1 ; h < 4 ; h++ )); do
x=$(awk -v i=h -v j=17 'FNR == 2 {printf "%s " $j}' newiptables.log)
echo $x
This is my file
Dec 26 09:17:51 localhost kernel: IN=eth0 OUT= MAC=00:10:c6:a8:da:68:00:90:7f:9c:50:5a:08:00 SRC=198.252.206.16 DST=10.128.1.225 LEN=313 TOS=0x00 PREC=0x00 TTL=64 ID=59334 PROTO=TCP SPT=80 DPT=56506 WINDOW=46535 RES=0x00 ACK PSH URGP=0
Dec 26 09:17:52 localhost kernel: IN=eth0 OUT= MAC=00:10:c6:a8:da:68:00:90:7f:9c:50:5a:08:00 SRC=198.252.206.16 DST=10.128.1.225 LEN=1440 TOS=0x00 PREC=0x00 TTL=64 ID=47303 PROTO=TCP SPT=80 DPT=56506 WINDOW=46535 RES=0x00 ACK URGP=0
Dec 26 09:17:52 localhost kernel: IN=eth0 OUT= MAC=00:10:c6:a8:da:68:00:90:7f:9c:50:5a:08:00 SRC=198.252.206.16 DST=10.128.1.225 LEN=1440 TOS=0x00 PREC=0x00 TTL=64 ID=47559 PROTO=TCP SPT=80 DPT=56506 WINDOW=46535 RES=0x00 ACK URGP=0

The problem is a missing comma in the printf command for awk:
awk -v i=h -v j=17 'FNR == 2 {printf "%s ", $j}' newiptables.log
^
|== This is needed
Quoting from the manual:
A simple printf statement looks like this:
printf format, item1, item2, ...

Related

Print sorted output with awk to avoid pipe sort command

I'm trying to match the lines containing (123) and then manipulate field 2 replacing x and + by space that will give 4 columns. Then change order of column 3 by Column 4.
To finally print sorted first by column 3 and second by column 4.
I'm able to get the output piping sort command after awk output in this way.
$ echo "
0: 1920x1663+0+0 kpwr(746)
323: 892x550+71+955 kpwr(746)
211: 891x550+1003+410 kpwr(746)
210: 892x451+71+410 kpwr(746)
415: 891x451+1003+1054 kpwr(746)
1: 894x532+70+330 kpwr(123)
324: 894x532+1001+975 kpwr(123)
2: 894x631+1001+330 kpwr(123)
212: 894x631+70+876 kpwr(123)
61: 892x1+71+375 kpwr(0)
252: 892x1+71+921 kpwr(0)" |
awk '/\(123\)/{b = gensub(/(.+)x(.+)\+(.+)\+(.+)/, "\\1 \\2 \\4 \\3", "g", $2); print b}' |
sort -k3 -k4 -n
894 532 330 70
894 631 330 1001
894 631 876 70
894 532 975 1001
How can I get the same output using only awk without the need to pipe sort? Thanks for any help.
Here is how you can get it from awk (gnu) itself:
awk '/\(123\)/{
$2 = gensub(/(.+)x(.+)\+(.+)\+(.+)/, "\\1 \\2 \\4 \\3", "g", $2)
split($2, a) # split by space and store into array a
# store array by index 3 and 4
rec[a[3]][a[4]] = (rec[a[3]][a[4]] == "" ? "" : rec[a[3]][a[4]] ORS) $2
}
END {
PROCINFO["sorted_in"]="#ind_num_asc" # sort by numeric key ascending
for (i in rec) # print stored array rec
for (j in rec[i])
print rec[i][j]
}' file
894 532 330 70
894 631 330 1001
894 631 876 70
894 532 975 1001
Can you handle GNU awk?:
$ gawk '
BEGIN {
PROCINFO["sorted_in"]="#val_num_asc" # for order strategy
}
/\(123\)$/ { # pick records
split($2,t,/[+x]/) # split 2nd field
if((t[4] in a) && (t[3] in a[t[4]])) { # if index collision
n=split(a[t[4]][t[3]],u,ORS) # split stacked element
u[n+1]=t[1] OFS t[2] OFS t[4] OFS t[3] # add new data
delete a[t[4]][t[3]] # del before rebuilding
for(i in u) # sort on whole record
a[t[4]][t[3]]=a[t[4]][t[3]] ORS u[i] # restack to element
} else
a[t[4]][t[3]]=t[1] OFS t[2] OFS t[4] OFS t[3] # no collision, just add
}
END {
PROCINFO["sorted_in"]="#ind_num_asc" # strategy on output
for(i in a)
for(j in a[i])
print a[i][j]
}' file
Output:
894 532 330 70
894 631 330 1001
894 631 876 70
894 532 975 1001
With collisioning data like:
1: 894x532+70+330 kpwr(123) # this
1: 123x456+70+330 kpwr(123) # and this, notice order
324: 894x532+1001+975 kpwr(123)
2: 894x631+1001+330 kpwr(123)
212: 894x631+70+876 kpwr(123)
output would be:
123 456 330 70 # ordered by the whole record when collision
894 532 330 70
894 631 330 1001
894 631 876 70
894 532 975 1001
I was almost done with writing and my solution was ditto as #anubhava's so adding a bit tweak to his solution :) This one will take care of multiple lines of same values here.
awk '
BEGIN{
PROCINFO["sorted_in"]="#ind_num_asc"
}
/\(123\)/{
$2 = gensub(/(.+)x(.+)\+(.+)\+(.+)/, "\\1 \\2 \\4 \\3", "g", $2)
split($2, a," ")
arr[a[3]][a[4]] = (arr[a[3]][a[4]]!=""?arr[a[3]][a[4]] ORS:"")$2
}
END {
for (i in arr){
for (j in arr[i]){ print arr[i][j] }
}
}' Input_file

How would I print the elements matching a regex pattern in order?

I have a text file that has text in this format:
ptr[0] = Alloc(1) returned 1000 (searched 1 elements)
Free List [ Size 1 ]: [ addr:1001 sz:99 ]
Free(ptr[0]) returned 0
Free List [ Size 2 ]: [ addr:1000 sz:1 ] [ addr:1001 sz:99 ]
ptr[1] = Alloc(7) returned 1001 (searched 2 elements)
Free List [ Size 2 ]: [ addr:1000 sz:1 ] [ addr:1008 sz:92 ]
Free(ptr[1]) returned 0
Free List [ Size 3 ]: [ addr:1000 sz:1 ] [ addr:1001 sz:7 ] [ addr:1008 sz:92 ]
ptr[2] = Alloc(5) returned 1001 (searched 3 elements)
Free List [ Size 3 ]: [ addr:1000 sz:1 ] [ addr:1006 sz:2 ] [ addr:1008 sz:92 ]
Free(ptr[2]) returned 0
Free List [ Size 5 ]: [ addr:1000 sz:1 ] [ addr:1001 sz:5 ] [ addr:1006 sz:2 ] [ addr:1008 sz:8 ] [ addr:1016 sz:84 ]
And I am trying to print out only the values that match with the sz: in the text file and print them in the order they are in but as a list. Like so:
$ awk -f list.awk file.txt | head
99
1 99
1 92
1 7 92
1 2 92
1 5 2 8 84
I've tried the following, but it prints out only the lines that contain sz:. How could I break it further to get the output I want?
/Free List/{
s = $0
split(s, a, /sz:/)
print s
}
Following awk solutions may help you on same.
Solution 1st: When you want only digit value associated with string sz then following may help you on same.
awk '{while(match($0,/sz:[0-9]+/)){val=(val?val FS:"") substr($0,RSTART+3,RLENGTH-3);$0=substr($0,RSTART+RLENGTH)}}val!=""{print val;val=""}' Input_file
Adding a non-one liner form of solution too now.
awk '
{
while(match($0,/sz:[0-9]+/)){
val=(val?val FS:"") substr($0,RSTART+3,RLENGTH-3);
$0=substr($0,RSTART+RLENGTH)}
}
val!=""{
print val;
val=""
}
' Input_file
Solution 2nd: In case you need to have string sz also with values then following may help you on same.
awk '{while(match($0,/sz:[0-9]+/)){val=(val?val FS:"") substr($0,RSTART+3,RLENGTH-3);$0=substr($0,RSTART+RLENGTH)}}val!=""{print val;val=""}' Input_file
Adding a non one liner form of solution too now.
awk '
{
while(match($0,/sz:[0-9]+/)){
val=(val?val FS:"") substr($0,RSTART+3,RLENGTH-3);
$0=substr($0,RSTART+RLENGTH)}
}
val!=""{
print val;
val=""
}
' Input_file
NOTE: In case you want to perform this operation only on those lines which havd string Free List then add /Free List/{ before while and add } before ' in above solutions simply.
if perl is okay:
$ perl -lne 'print join " ", /sz:(\d+)/g if /Free List/' ip.txt
99
1 99
1 92
1 7 92
1 2 92
1 5 2 8 84
if /Free List/ if line contains Free List
/sz:(\d+)/g match all digits that follows sz:
print join " " print those matches separated by space
see https://perldoc.perl.org/perlrun.html#Command-Switches for details on -lne options
Using bash and grep:
while IFS= read -r line; do
x=$(grep -oP '(sz:\K\d+)' <<< "$line")
[[ $x ]] && echo $x
done < file
Output :
99
1 99
1 92
1 7 92
1 2 92
1 5 2 8 84
With GNU awk for FPAT:
$ awk -v FPAT='sz:[0-9]+' '{for (i=1;i<=NF;i++) printf "%s%s", substr($i,4), (i<NF?OFS:ORS)}' file
99
1 99
1 92
1 7 92
1 2 92
1 5 2 8 84
With any awk:
$ awk '{out=""; while (match($0,/sz:[0-9]+/)) { out = (out=="" ? "" : out OFS) substr($0,RSTART+3,RLENGTH-3); $0=substr($0,RSTART+RLENGTH) } $0=out } NF' file
99
1 99
1 92
1 7 92
1 2 92
1 5 2 8 84

awk to match field between two files and use conditions on match

I am trying to look for $2 of file1 (skipping the header) in $2 of file2 and if they match and the value in $10 is > 30 and $11 is > 49, then print the line to a output file. The below awk has syntax errors in it though shellcheck didn't return any. Both the input and output are tab-delimited. I think the below is close, but not sure what is wrong. Thank you :).
awk
awk -F'\t' -v OFS='\t' 'NR==FNR{A[$2];next}$2 in A
{if($10 >.5 OFS $11 > 49)
print ; next
' file1 file2
awk: cmd. line:2: {if($10 >.5 OFS $11 > 49)
awk: cmd. line:2: ^ syntax error
awk: cmd. line:3: print ; next
awk: cmd. line:3: ^ unexpected newline or end of string
file1
Missing in IDP but found in Reference:
2 166848646 G A exonic SCN1A 68 13 16;20 0;0 17;15 0;0 0;0 0;0 c.[5139C>T]+[=] 52.94
file2
chr2 166245425 SCN2A AMPL5155065355 SNP Het C/T C T 54 100 50 23 27
chr2 166848646 SCN1A AMPL1543060606 SNP Het G/A G A 52.9411764706 100 68 32 36
desired output
2 166848646 G A exonic SCN1A 68 13 16;20 0;0 17;15 0;0 0;0 0;0 c.[5139C>T]+[=] 52.94
edit with new awk
awk -F'\t' -v OFS='\t' 'NR==FNR{A[$2];next}$2 in A {
if($10 >.5 OFS $11 > 49) >>> if($10 >.5 && $11 > 49)
print }
' file1 file2 > out
awk: cmd. line:2: if($10 >.5 OFS $11 > 49) >>> if($10 >.5 && $11 > 49)
awk: cmd. line:2: ^ syntax error
here you go...
$ awk 'BEGIN{FS=OFS="\t"} NR==FNR{a[$2]; next}
($2 in a) && $10>30 && $11>49 ' file1 file2

MAC adressess found on multiple ports

I have processed an access log with :
grep -o -w -E '[[:alnum:]:]{17}.*[0-9]' testlog | awk '{print $1 " " $3}'
Which results in the following (obfuscated)
1.01:03:96:51:9A:31 3:37
2.01:03:96:51:9A:31 3:39
3.00:E0:2B:00:00:01 3:39
4.3C:A9:F4:1C:68:A4 3:37
5.01:01:96:51:A6:5E 3:39
6.01:01:96:51:A6:5E 3:39
How do I print all MACs that are found on multiple ports (in the example ) rows 1-2 and 5-6 ?
If you have this input:
cat file
01:03:96:51:9A:31 3:37
01:03:96:51:9A:31 3:39
00:E0:2B:00:00:01 3:39
3C:A9:F4:1C:68:A4 3:37
01:01:96:51:A6:5E 3:39
01:01:96:51:A6:5E 3:39
You can do
awk '!seen[$0]++' file
01:03:96:51:9A:31 3:37
01:03:96:51:9A:31 3:39
00:E0:2B:00:00:01 3:39
3C:A9:F4:1C:68:A4 3:37
01:01:96:51:A6:5E 3:39
or
awk '!seen[$0]++' file | sort -k2
01:03:96:51:9A:31 3:37
3C:A9:F4:1C:68:A4 3:37
00:E0:2B:00:00:01 3:39
01:01:96:51:A6:5E 3:39
01:03:96:51:9A:31 3:39
Do you need like this?
awk '{ seen[$1]++; } END{ for(idx in seen){ if(seen[idx] != 1 ) print idx }}' file
Input file:
01:03:96:51:9A:31 3:37
01:03:96:51:9A:31 3:39
00:E0:2B:00:00:01 3:39
3C:A9:F4:1C:68:A4 3:37
01:01:96:51:A6:5E 3:39
01:01:96:51:A6:5E 3:39
Output:
$ awk '{ seen[$1]++; } END{ for(idx in seen){ if(seen[idx] != 1 ) print idx }}' file
01:01:96:51:A6:5E
01:03:96:51:9A:31
You never need grep if you're using awk. You don't show what your original testlog looks like so while this will probably produce the output you want, it may not be the best way to do it since it's just using the logic from your grep but there may be a better way in awk:
awk '
match($0,/[[:alnum:]:]{17}.*[0-9]/) {
ip = substr($0,RSTART,RLENGTH)
print ip
sub(/ .*/,"",ip)
count[ip]++
}
END {
for (ip in count) {
if (count[ip] > 1) {
printf "IP %s occurs %d times\n", ip, count[ip] | "cat>&2"
}
}
}
' testlog

Unable to combine words in Files by Zsh/AWK

I have File1
A,B,C
and File2
D,E,F
I am trying to have
AD, AE, AF, BD, BE, BF, CD, CE, CF
unsuccessfully by
echo {`cat File1`}{`cat File2`}
giving
{A,B,C}{D,E,F}
How can you solve the problem by Zsh/AWK?
awk -F, '
NR==FNR {
# read lines from File1 into the array f1
f1[NR]=$0
next
}
{
# foreach line in File2
split(f1[FNR], words); # get words from corresponding line in File1
sep = ""
for (i in words) {
for (j=1; j<=NF; j++) {
printf("%s%s%s", sep, words[i], $j)
sep = ", "
}
}
print ""
}
' File1 File2
If File1 contains
A,B,C
1,2,3
and File2 contains
D,E,F
4,5,6
then the awk script outputs
AD, AE, AF, BD, BE, BF, CD, CE, CF
14, 15, 16, 24, 25, 26, 34, 35, 36
I don't know zsh, here's what I did with bash and sed:
echo "A,B,C" >a
echo "D,E,F" >b
for i in `cat a | sed -e "s#,#\n#g"`;
do for j in `cat b | sed -e "s#,#\n#g"`;
do echo -n "$i$j, ";
done ;
done | sed -e "s#,\s\$##"
The output then is:
AD, AE, AF, BD, BE, BF, CD, CE, CF