====== AWK ====== **AWK**: Abkürzung, die aus den Anfangsbuchstaben der Nachnamen der drei "Väter" der Sprache AWK besteht: Al **A**ho, Peter **W**einberger, Brian **K**ernighan * **[[https://de.wikibooks.org/wiki/Awk:_Grundlagen:_Aktionen]]** * [[http://www.net-braun.de/etc/awk-kurs.htm]] * [[http://mikiwiki.org/wiki/awk/Einstieg_in_awk]] * [[http://www-e.uni-magdeburg.de/urzs/awk/awk.shtml]] * [[http://www.thomashertweck.de/sedawk.html]] * [[http://www.clug.de/vortraege/awk/index.html]] * [[http://www.christinewolfinger.de/Seminare/15AWKF.pdf]] * [[http://www.unix-manuals.com/refs/awk/awk.htm]] * [[http://www.linupedia.org/opensuse/Awk#Die_Aktion]] * **[[https://www.ostc.de/awk.pdf]]** * **[[https://www.gnu.org/software/gawk/manual/html_node/String-Functions.html]]** > echo -e "1\n2\n3" 1 2 3 > echo -e "1\n2\n3" | awk 'NR==2' 2 > echo "1 2 3" | awk '{print $2}' 2 > echo "1:2:3" | awk -F':' '{print $2}' 2 > echo "foooobazbarrrrr" | awk '{print $1}' foooobazbarrrrr > echo "foooobazbarrrrr" | awk '{ match($0, /(fo+).+(bar*)/, arr); print arr[1], arr[2] }' foooo barrrrr ==== Runden ==== echtes runden mit awk: > echo "5.56" | awk '{printf "%.1f\n", $1}' 5.6 > echo "5.56" | awk '{printf "%.0f\n", $1}' 6 abrunden mit awk: > echo "5.56" | awk '{printf("%u\n", $1)}' 5 Zahlen mit fester Länge ausgeben: > echo "5.56" | awk '{printf("%.8u\n", $1)}' 00000005 ==== Pi ==== > echo | awk '{print 4 * atan2(1,1)}' 3.14159 # "AWK" ist nur bis zur 15. Nachkommastelle genau > echo | awk '{printf "%.15f\n", 4 * atan2(1,1)}' 3.141592653589793 ==== Zahlen summieren ==== Zusammenzählen aller Werte in der 1. Spalte ("$1"): # awk '{zwischenwert=$1;summe+=zwischenwert}END{print "Gesamtwert:",summe}' # awk '{zwischenwert=$1; summe += zwischenwert } END { print "Gesamtwert:", summe }' # awk '{zwischenwert=$1; summe += zwischenwert } END { printf("%li", summe)}' # awk '{z=$1;s+=z}END{printf("%li", s)}' # awk '{z=$1;s+=z}END{print s}' # awk '{z=$1;s+=z}END{printf("%.2f\n",s/1024^3)}' Durchschnitt aller Werte der letzten Spalte: # cat ... | nl | awk '{ zwischenwert=$NF; summe += zwischenwert } END { print summe/$1 }' ==== Schleife ==== * [[https://im-coder.com/wie-awk-verwenden-um-spalten-in-einer-schleife.html]] * [[https://www-user.tu-chemnitz.de/~hot/unix_linux_werkzeugkasten/awk.html]] # echo "7" | awk '{for (i=1;i<=$1;i++) print i }' 1 2 3 4 5 6 7 ==== Wurzel ziehen ==== Wurzel aus 9 > echo "9" | awk '{print sqrt($1)}' 3 > echo "10 1" | awk '{print sqrt($1-$2)}' 3 5. Wurzel aus 4 ([[https://frageit.de/questions/42775094/nth-root-in-bash]]): > awk 'BEGIN { print (4 ** (1.0 / 5)) }' 1.31951 > awk 'BEGIN { print (ARGV[2] ** (1.0 / ARGV[1])) }' 5 4 1.31951 ==== Logarithmus ==== natürlicher Logarithmus aus 5: > echo "5" | awk '{print log($1)}' 1.60944 natürlicher Logarithmus aus 20: > echo "20" | awk '{print log($1)}' 2.99573 dezimaler Logarithmus 1000: > echo "1000" | awk '{print log($1)/log(10)}' 3 binärer Logarithmus aus 8: > echo "8" | awk '{print log($1)/log(2)}' 3 ==== IF-Verzweigung ==== Mit Alphanumerischen Zeichen: # echo 'WPA2' | awk '{proto="nix"; if ($1 == "WPA") proto="WPA"; if ($1 == "WPA2") proto="RSN"; print proto}' RSN die "if"-Anweisung von AWK kann sogar mit Kommastellen umgehen: # echo "1.5" | awk '{if ($1 < 3) GR="gross" ; if ($1 < 2) GR="mittel" ; if ($1 < 1) GR="klein" ; print $1"="GR}' 1.5=mittel # echo "1.5" | awk '{if ($1 < 3) GR=3 ; if ($1 < 2) GR=2 ; if ($1 < 1) GR=1 ; print GR}' 2 # echo "2" | awk '{if ($1 == 1) GR="klein" ; if ($1 == 2) GR="mittel" ; if ($1 == 3) GR="gross" ; print GR}' mittel === IP => Integer === # vi ip2int.sh #!/bin/bash echo "${1}" | awk -F'.' '{print $1"\n"$2"\n"$3"\n"$4}' | nl | while read N Z do echo "${N} ${Z}" | awk '{lfdn=$1 ; if (lfdn == 1) f='256*256*256' ; if (lfdn == 2) f='256*256' ; if (lfdn == 3) f=256 ; if (lfdn == 4) f=1 ; print f*$2}' done | awk '{z=$1;s+=z}END{print s}' # /bin/bash ip2int.sh 192.168.1.100 3232235876 # /bin/bash ip2int.sh 255.255.255.0 4294967040 === Integer => Binär === # echo "$1" | awk '{for (i=0; $1>=2^i; i++); i--; Z=2^i} END {print i,Z}' exit # solange die erste Zahl >=0 ist, weitermachen: # /var/tmp/test2.sh 4294967040 31 2147483648 # echo "4294967040-2147483648"|bc -l 2147483392 # /var/tmp/test2.sh 2147483392 30 1073741824 Das mach ich später fertig, hab jetzt keine Zeit mehr... FIXME ==== Zahlen formatiert ausgeben ==== c Ein ASCII-Zeichen. Ist das entsprechende Argument eine Zahl, so wird diese als Kode im Zeichensatz interpretiert und ausgegeben. d Eine vorzeichenbehaftete Integerzahl i Eine vorzeichenbehaftete ganze Zahl. e Eine Gleitkommazahl der Form: [-]d.ddddddE[+-]dd f Eine Gleitkommazahl der Form: [-]ddd.dddddd g das Argument wird entweder wie e oder f umgewandelt. Je nachdem, welches Ergebnis kürzer ist. Nicht signifikante Nullen werden dabei unterdrückt. o Eine vorzeichenlose Oktalzahl. s Ein String. u Eine vorzeichenlose ganze Zahl. x Eine vorzeichenlose Hexadezimalzahl mit Kleinbuchstaben. X Eine vorzeichenlose Hexadezimalzahl mit Großbuchstaben. % Hebt die bedeutung von % auf. Also %% gibt % aus. abs(x) Funktion zum ausgeben vom Absolutwert von x int(x) Ganzzahliger Anteil von x log(x) natürlicher Logarithmus von x rand() Eine Zufallszahl z: 0 <= z < 0 sqrt(x) Wurzel aus x # echo "10 50" | awk 'func abs(x) {return ((x < 0.0) ? -x : x)} {printf "%.0f\n", abs($1 - $2)}' # echo "1021 576" | awk '{printf("%d %d\n",($1/($1/720))/2,($2/($1/768))/2)}' # echo "1021 576" | awk '{printf("%i %i\n",($1/($1/720))/2,($2/($1/768))/2)}' # echo "1021 576" | awk '{printf("%u %u\n",($1/($1/720))/2,($2/($1/768))/2)}' # echo "1021 576" | awk '{printf("%uk\n",$1*$2*1000/69120)}' **__Rechengenauigkeit__** # echo "76.1 81.8 3.1415926535897932384626433832795028841971693993751" | awk '{print ($2/2)^2*$3*$1/1000}' 399.927 # echo "76.1 81.8 3.1415926535897932384626433832795028841971693993751" | awk '{OFMT="%.16G";print ($2/2)^2*$3*$1/1000}' 399.9273868814023 # echo "76.1 81.8 3.1415926535897932384626433832795028841971693993751" | awk '{OFMT="%.13f";print ($2/2)^2*$3*$1/1000}' 373,9770000000000 # echo "76.1 81.8 3.1415926535897932384626433832795028841971693993751" | awk '{OFMT="%.14f";print ($2/2)^2*$3*$1/1000}' 373,97699999999998 # echo "76.1 81.8 3.1415926535897932384626433832795028841971693993751" | awk '{OFMT="%.23G";print ($2/2)^2*$3*$1/1000}' 399.92738688140229896817 # echo "(81.8/2)^2 * 3.1415926535897932384626433832795028841971693993751 * 76.1 / 1000" | bc -l 399.92738688140234827240 ---- Aus einer Datei soll die letzte Spalte von allen Zeilen ausgegeben werden, die "78378" aber nicht "sftp" enthalten: # export SUCHPATTERN="78378" # awk '/'$SUCHPATTERN'/ && !/sftp/ {printf("%s ", $NF);}' [Datei] # awk '/78378/ && !/sftp/ {printf("%s ", $NF);}' [Datei] Es entspricht dem Aufruf: # cat [Datei] | grep '78378' | grep -v 'sftp' | awk '{printf("%s ", $NF);}' Die letzte Spalte an den Anfang setzen, egal wieviel Spalten die Zeile hat: # awk '{ printf $NF;$NF = "" ;printf " "$0"\n" }' Wenn die Spalte 4 groesser ist als Spalte 6 oder wenn in Spalte 7 eine Zahl steht, die groesser als 30000 ist, wird die Zeile ausgegeben: # awk '$4 < $6 || $7 > 30000' Es wird die Zeile ausgegeben, in der von Spalte 7, die Groesste Zahl steht: # vi script.awk max < $7 { max = $7 ; stadt = $1 } END { print "Die meisten Zuschauer waren in",stadt,"("max")." } # awk -v max=0 -f script.awk mit AWK alles in Großbuschtaben umwandeln: # echo "buchstaben" | awk '{print toupper($0)}' BUCHSTABEN mit AWK alles in Kleinbuschtaben umwandeln: # echo "BUCHSTABEN" | awk '{print tolower($0)}' buchstaben AWK als grep: # awk '/PATERN/' # alle Zeilen mit "PATTERN" # awk '/^[0-9]+/ {print $1}' # das erste Wort aller Zeilen mit einer Ziffer am Anfang # awk '/^[0-9]+|^#/' # alle Zeilen mit einer Ziffer oder "#" am Anfang # awk '/[^n]{print $1}' # es werden nur Zeilen verarbeitet, in denen kein "n" vorkommt # awk 'lenght() > 50 {print}' # nur Zeilen die laenger als 50 Zeichen sind AWK als sed: # awk '/Anfang/,/Ende/' # Textabschnitte von "Anfang" bis "Ende" # awk -v neu="-" '{$2 = neu;print};' # zweite Spalte wird durch "-" ersetzt # awk -v alt='_' -v neu='-' '{sub(alt, neu); print}' # einfache Ersetzung # awk -v alt='_' -v neu='-' '{gsub(alt, neu); print}' # globale Ersetzung # awk '{alt="_"; neu="-"} {gsub(alt, neu); print}' # globale Ersetzung Beispiel: * rein: D00D2 00010 31/12/2008 102 * raus: D00D2 00010 20081231 102 # awk '{ alt="/"; neu=" "; gsub(alt, neu); $1; $2; $5$4$3; $6 print }' Es sollen nur die Leerzeichen der Variablen (vor dem Gleichheitszeichen) gegen Unterstriche ausgetauscht werden, nicht jedoch die Leerzeichen der Werte (hinter dem Gleichheitszeichen). Dazu wird vor der Veränderung der Wert in eine Variable gespeichert: # echo "CPU Signature=Type 0, Family 6, Model 26, Stepping 5" | awk -F'=' '{wert=$2 ; gsub(" ", "_") ; print $1"="wert}' CPU_Signature=Type 0, Family 6, Model 26, Stepping 5 Und jetzt noch zusätzlich die Variablen von Großschreibung in Kleinschreibung umwandeln. # echo "CPU Signature=Type 0, Family 6, Model 26, Stepping 5" | awk -F'=' '{wert=$2 ; gsub(" ", "_") ; variable=tolower($1) ; print variable"="wert}' cpu_signature=Type 0, Family 6, Model 26, Stepping 5 Zeilenlängen einer Datei ermitteln # wc -L [dateiname].csv # awk '{print length;exit}' [dateiname].csv 78 CSV (comma separated version) in "feste Spaltenbreite" umwandeln 12 Zeichen breite Spalte generieren: "%12s" # awk -F';' '{printf("%12s%9s%13s%14s%9s\n", $1,$2,$5,$6,$7)}' daten.txt spalte1 spalte2 spalte5 spalte6 Spalte7 13 1 092007 01/31/2099 al 52 1 092009 08/31/2009 al 62 1 092009 08/31/2009 al 64 1 092009 08/31/2009 al 71 1 092009 08/31/2009 al 85 1 092009 08/31/2009 al # Da wo das Komma steht, werden die Freiräume mit Leerzeichen # aufgefüllt. # echo "Spalte01|Spalte02|Spalte03|Spalte4|Spalte5|Spalte6" | awk -F'|' '{printf ("%10s%16s%16s%16s%24s%21s\n", $1" | ",$2" | ",$3" | ",$4" | ",$5" | ",$6)}' allerdings sind die Einträge in der ersten Spalte immer __rechtsbündig__: > echo "innodb_file_per_table ON" | awk '{printf("%47s%47s\n",$1" = ",$2)}' innodb_file_per_table = ON will man sie __linksbündig__, dann kann man das so machen: > echo "innodb_file_per_table ON" | rev | awk '{printf("%47s%47s\n",$1" = ",$2)}' | rev | sed 's/[ ]*$//' innodb_file_per_table = ON ---- Muss man einen String innerhalb eines Stringes (Unterstring => SubString) zerlegen, also zum Beispiel ein Datum in einem Satz umformen, dann bietet sich die Funktion //substr// in //AWK// an: echo "Das Datum 21.03.1969 im klassischen Format." | awk '{print $1,$2,substr($3,7,4)"-"substr($3,4,2)"-"substr($3,1,2),$4,"Euro-"$6}' Das Datum 1969-03-21 im Euro-Format. //substr// benötigt drei Parameter: - "Spalte" bzw. "Wort" aus dem Originalstring; - ab dem wievielten Zeichen von dieser "Spalte" bzw. "Wort" wieder gegeben werden soll; - wieviel Zeichen aus dieser "Spalte" bzw. "Wort" wieder gegeben werden sollen; die ersten beiden Zeichen einer Zeichenkette ausgeben: echo "${STRING}" | awk '{print substr($1,1,2)}' die letzten beiden Zeichen einer Zeichenkette ausgeben: echo "${STRING}" | awk '{a=length($1)-1; print substr($1,a,2)}' ---- * [[http://www.pement.org/awk/awk1line.txt]] Will man zum Beispiel die Werte "cpu family", "model" und "stepping" einer CPU unter Linux auslesen, dann kann man einfach in dieser Datei nachsehen: # cat /proc/cpuinfo processor : 0 vendor_id : GenuineIntel cpu family : 15 model : 2 model name : Intel(R) Pentium(R) 4 CPU 2.60GHz stepping : 9 cpu MHz : 2593.547 cache size : 512 KB fdiv_bug : no hlt_bug : no f00f_bug : no coma_bug : no fpu : yes fpu_exception : yes cpuid level : 2 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe up pebs bts cid xtpr bogomips : 5187.09 clflush size : 64 power management: Hat diese CPU allerdings mehr als einen Kern, dann wird es schnell unübersichtlich: # cat /proc/cpuinfo processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 26 model name : Intel(R) Xeon(R) CPU X5570 @ 2.93GHz stepping : 5 cpu MHz : 2926.105 cache size : 8192 KB physical id : 1 siblings : 8 core id : 0 cpu cores : 4 apicid : 16 initial apicid : 16 fpu : yes fpu_exception : yes cpuid level : 11 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology pni dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 lahf_lm ida tpr_shadow vnmi flexpriority ept vpid bogomips : 5852.21 clflush size : 64 cache_alignment : 64 address sizes : 40 bits physical, 48 bits virtual power management: ... processor : 15 vendor_id : GenuineIntel cpu family : 6 model : 26 model name : Intel(R) Xeon(R) CPU X5570 @ 2.93GHz stepping : 5 cpu MHz : 2926.105 cache size : 8192 KB physical id : 0 siblings : 8 core id : 3 cpu cores : 4 apicid : 7 initial apicid : 7 fpu : yes fpu_exception : yes cpuid level : 11 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology pni dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 sse4_2 lahf_lm ida tpr_shadow vnmi flexpriority ept vpid bogomips : 5851.96 clflush size : 64 cache_alignment : 64 address sizes : 40 bits physical, 48 bits virtual power management: Die oben gezeigte Ausgabe stammt von einem Dual-Quad-Core mit Hyper-Threading. Also sind es nur zwei CPUs mit je vier Kernen, die über erweiterte Register verfühgt, wodurch der einzelne Kern zwischen zwei Pipes schneller hin und her springen kann. Hyper-Threading bringt nur einen Geschwindigkeitsvorteil, wenn man mehr Prozesse laufen lassen möchte als sich CPU-Kerne im System befinden! An sonsten werden z.B. die ersten beiden Prozesse auf die ersten beiden virtuellen CPUs gestartet, wobei das aber der selbe Kern (mit Hyper-Threading) ist und alle anderen CPU-Kerne haben nix zu tun. Deshalb sollte man HT (Hyper-Threading) im BIOS nur dann aktivieren, wenn man vilele Prozesse fährt. Möchte man - nur diese drei Parameter und - nur vom ersten CPU-Kern-Block (also nur ein einziges mal) dann kann man sich alles, bis zu der Zeile anzeigen lassen, in der "stepping" das erste mal vorkommt: sed -e '/stepping/ q' /proc/cpuinfo processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 26 model name : Intel(R) Xeon(R) CPU X5570 @ 2.93GHz stepping : 5 Das ist schon sehr viel übersichtlicher und enthält auch alle Informationen, die wir brauchen. Allerdings werden auch da noch viele unnütze Infos angezeigt, die rausgefiltert werden sollen. Filtert man jetzt nach der klassischen Art mit "grep", dann könnte das so aussehen: # sed -e '/stepping/ q' /proc/cpuinfo | egrep 'cpu family|model|stepping' | fgrep -v 'model name' | awk '{print $NF}' 6 26 5 Das gleiche Ergebnis kann man allerdings auch bekommen, wenn man an Stelle von drei Prozessen (egrep, fgrep und awk) nur einen aufruft (awk): # sed -e '/stepping/ q' /proc/cpuinfo | awk '/cpu family|model|stepping/ && !/model name/ {print $NF}' 6 26 5 Denn //AWK// kann alles, was grep kann.