Benutzer-Werkzeuge

Webseiten-Werkzeuge


awk

AWK

AWK: Abkürzung, die aus den Anfangsbuchstaben der Nachnamen der drei "Väter" der Sprache AWK besteht: Al Aho, Peter Weinberger, Brian Kernighan

> 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

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:

  1. "Spalte" bzw. "Wort" aus dem Originalstring;
  2. ab dem wievielten Zeichen von dieser "Spalte" bzw. "Wort" wieder gegeben werden soll;
  3. 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)}'

—-

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

  1. nur diese drei Parameter und
  2. 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.

/home/http/wiki/data/pages/awk.txt · Zuletzt geändert: von manfred