AWK er beregnet til scanne og behandle tekst inddata, og give tekst som uddata.
AWK bruger "regular expressions" til at matche tekst, og er langt
simplere end f.eks. perl , tcl ,
og basic til disse opgaver. AWK er dog mere
avanceret end grep og sed .
AWK er speciel god til opgaver der går ud på at matche tekst,
konvertere tekstfiler fra et format til et andet,
eventuelt undervejs med optællinger, opsummeringer osv.
AWK er oprindelig designet og implementeret i 1977 af Alfred Aho,
Peter Weinberger, and Brian Kernighan fra AT&T Bell Labs.
Sidstnævnte fortsætter med at vedligeholde og forbedre AWK.
Den version man normalt stifter kendskab med er gawk som er GNU versionen
af awk, og som har en del udvidelser i forholdt til standard AWK.
Man skal være opmærksom på at udvidelserne i gawk ikke vil virke med
en rigtig standard "awk" som ofte følger med en kommerciel unix.
Manualsiden til gawk(1) har et afsnit "GNU EXTENSIONS" som beskriver disse udvidelser. Se også infosiderne med info gawk .
Vil man kun anvende kode der følger POSIX standarden så
kan man kalde GAWK med "gawk --posix .... ".
Ofte er awk på et GNU/linux system blot et symbolsk link til gawk:
lrwxrwxrwx 1 root root 4 May 30 2000 /usr/bin/awk -> gawk
Awk programmer minder lidt om C i syntaks, og består af en
valgfrit begyndelsesblok som udføres før inddata læses:
BEGIN {
<kommandoer>
}
Derefter et antal blokke med en valfri betingelse eller regular expression foran:
{ # uden , så udføres det altid
}
<regular expression> {
}
<expression> {
}
Til sidst en valgfri afsluttende blok der udføres efter alt inddata er læst:
END {
}
Som standard læses inddata som poster hvor hvert post består af felter adskilt
af blanktegn (white space), og linieskift som afslutning på en post.
Variable oprettes efter behov, og det afgøres
når variablen bruges om det skal fortolkes som et tal eller tekst.
-
Et lille eksempel er ex1.awk :
# lidt initialisering,
# egentlig overflødig, da alle nye variable er initielt nul eller blank.
BEGIN { linier=0; ord=0; }
{
ord += NF; # NF = Number of Fields, dvs. antal ord
linier++;
}
END {
print "læst",ord,"ord og",linier,"linier";
# NR indholder aktuel post nummer, dvs. nu antal poster læst:
print "NR=",NR;
}
Man kan kalde f.eks. awk med gawk -f ex1.awk {filnavn(e)}
Sammenligning resultatet med wc {filnavn(e)} .
Wc er programmet (wordcount) der tæller antal
tegn, ord, linier mv.
Det er sådan at de forskellige felter i inddata nummereres fra $1.
Variablen NF angiver antallet, og $NF er sidste felt.
-
En enkel opgave er f.eks. at pille en given kolonne ud af noget uddata,
som f.eks. optælling af bytes i uddata fra ls -l .
ls -l | \
awk '{ sz += $5; } END { print "bytes=",sz,"bytes", sz/1024,"kbytes"; }'
Det kan også laves som alias i csh/tcsh, eller en kombineret alias og
function i bash så man kan lave sin egen "filesize" kommando:
function filesize_f() {
ls --color=none -oldG $* | awk '{printf("%8d %s\n",$4,$8);}'
}
alias filesize=filesize_f
Denne kan passende placeres i sin bash opstartsfil.
-
En anden opgave er en liste af programmer i ps , men
hvor kun selve programnavnet listes:
ps | awk '{ print $4; }'
-
Nu ville vi måske hellere have alle ens programnavne samlet, og til
dette kan man anvende de indbyggede arrays, der indekseres med en
tekststreng. Resultat skal sorteres efter hyppighed, og til det
anvendes det eksterne program sort :
ps | \
awk '{ prog[$4] += 1; } END { for (pr in prog) print prog[pr],pr;}' \
| sort -r
-
Der ønskes en liste af alle almindelige brugere på ens unix system udfra
/etc/passwd. Der skal kun vises brugernummer, brugernavn og fuld navn.
Der udnyttes at brugernumre i de fleste linux distributioner normalt
starter over 500 eller 1000.
Da felterne i password filen er adskilt med ":" så anvendes en
ny feltadskiller, FS=":" .
Den kunne også sættes med awk -f ":" når awk kaldes.
awk ' BEGIN {FS=":";} $3>=500 { print $3" "$1":"$5;}' /etc/passwd
-
Vis hvilke grupper brugeren frank er medlem af:
awk ' BEGIN {FS=":";} $4 ~ /frank/ { print $3,$1}' /etc/group
-
Konverter passwordfilen til en
kommafil, dvs. CSV fil (comma separated values).
En CSV fil kan indlæses af de fleste regneark og databaseprogrammer.
BEGIN {
FS=":";
print "\"login\",\"userID\",\"groupID\",\"name\",\"home\",\"shell\"";
}
{
printf("\"%s\",\"%s\",%s,%s,\"%s\",\"%s\",\"%s\"\n",$1,$"x"$3,$4,$5,$6,$7);
}
-
Når vi nu er ved CSV filer, så kan awk også sættes til at læse disse:
BEGIN { FS="," }
{
for (i=1; i>=NF; i++) {
printf("[%s] ",$i);
}
print "";
}
En ulempe i ovenstående er dog at felter i inddata ikke må indeholde ","
da det opfattes som skilletegn .
-
Hidtil er der ikke anvendt regular expressions, så her kommer et
eksempel med simpel brug af disse.
Eksemplet indsætter en tekstfil i et html dokument i mellem linierne med
<--! INCL BEGIN --> og
<--! INCL END --> .
Samtidig konverteres tegnene & , < og > til hhv. & , < , > .
Programmet kan anvendes til at isætte/opdatere en html fil med en
tekst der ændrer sig. Eventuelt kan denne fil være uddata fra et
andet program.
htmlupd :
#!/bin/sh
#
# Eksempel til SSLUG's julekalender den 23 december 2001
#
# Programmet indsæter en tekst i en html fil, og konverter samtidig
# tegn som &, < og > til & < og >
#
# Awk skriptet er indbygget i shellscriptet idet den udnytter
# at '...' kan fortsætte over flere linier:
# gawk ' ...program ' filnavn
#
# Programmet skal dog være mindre end den maksimale kommandolinielængde.
#
update_html() {
# kaldes med
# update_html [HTML-FIL] [TEKSTFIL]
if [ -r "$1" -a -r "$2" ] ; then
gawk -vinclfile="$2" '
BEGIN { state=1; }
function tohtml(tmpline) {
# simpel omskrivning af &, < og > til HTML
gsub(/&/,"\\&",tmpline);
gsub(/</,"\\<",tmpline);
gsub(/>/,"\\>",tmpline);
return tmpline;
}
function read_incl(fname) {
#indlæs [fname], konverter tegn til html og udskriv
while ( (st=(getline < fname))>0 ) {
print tohtml($0);
}
}
/<\!-- INCL BEGIN/ {
# har "INCLBEGIN", indsæt inclfile
state=2;
print $0;
print "<!-- added file=",inclfile," -->" ;
read_incl(inclfile) ;
next ;
}
/<\!-- INCL END/ {
# har "INCL END", fortsæt med resten af filen
state=3;
print ; # tom "print" svarer til "print $0"
next ;
}
{ # kopier inddata, udtagen mellem "INCL BEGIN" og "INCL END"
if ( state != 2 ) print ;
next ;
}
' $1
fi
}
html=htmlupd.html
oldhtml=htmlupd.old.html
tmpincl=htmlupd # som eksempel brug dette shellscript som inddata
tmptmp=tmpupd_$$.html
update_html "$html" "$tmpincl" > "$tmptmp"
if [ -r "$tmptmp" ] ; then
mv "$html" "$oldhtml"
mv "$tmptmp" "$html"
else
echo "error, can not read file: $tmptmp"
echo "no files changed"
fi
htmlupd.html :
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
"http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
<head>
<title>Eksempel til Julekalender 23. dec 2001</title>
</head>
<body style="background-color:white; color:steelblue;">
<h3>Eksempel til SSLUG's Julekalender 23. dec 2001</h3>
<pre style="color:#000033; background-color:#EEEEDD;">
<!-- INCL BEGIN -->
<!-- INCL END -->
</pre>
</body>
</HTML>
|