LINUX-AWKチュートリアル


スタートアップnetstatコマンドから次の情報を例に抽出しました.
$ cat netstat.txt
Proto Recv-Q Send-Q Local-Address          Foreign-Address             State
tcp        0      0 0.0.0.0:3306           0.0.0.0:*                   LISTEN
tcp        0      0 0.0.0.0:80             0.0.0.0:*                   LISTEN
tcp        0      0 127.0.0.1:9000         0.0.0.0:*                   LISTEN
tcp        0      0 coolshell.cn:80        124.205.5.146:18245         TIME_WAIT
tcp        0      0 coolshell.cn:80        61.140.101.185:37538        FIN_WAIT2
tcp        0      0 coolshell.cn:80        110.194.134.189:1032        ESTABLISHED
tcp        0      0 coolshell.cn:80        123.169.124.111:49809       ESTABLISHED
tcp        0      0 coolshell.cn:80        116.234.127.77:11502        FIN_WAIT2
tcp        0      0 coolshell.cn:80        123.169.124.111:49829       ESTABLISHED
tcp        0      0 coolshell.cn:80        183.60.215.36:36970         TIME_WAIT
tcp        0   4166 coolshell.cn:80        61.148.242.38:30901         ESTABLISHED
tcp        0      1 coolshell.cn:80        124.152.181.209:26825       FIN_WAIT1
tcp        0      0 coolshell.cn:80        110.194.134.189:4796        ESTABLISHED
tcp        0      0 coolshell.cn:80        183.60.212.163:51082        TIME_WAIT
tcp        0      1 coolshell.cn:80        208.115.113.92:50601        LAST_ACK
tcp        0      0 coolshell.cn:80        123.169.124.111:49840       ESTABLISHED
tcp        0      0 coolshell.cn:80        117.136.20.85:50025         FIN_WAIT2
tcp        0      0 :::22                  :::*                        LISTEN

以下に、最も簡単で最も一般的なawkの例を示します.その出力は1列目と4例目です.
ここで、単一引用符の中の括弧で囲まれているのはawkの文であり、単一引用符でのみ含まれることに注意してください.そのうちの1..nは第数例を表す.注意:$0は行全体を表します.
$ awk '{print $1, $4}' netstat.txt Proto Local-Address tcp 0.0.0.0:3306
tcp 0.0.0.0:80
tcp 127.0.0.1:9000
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp coolshell.cn:80
tcp :::22

awkのフォーマット出力を見てみましょう.C言語のprintfと変わらないです.
$ awk '{printf "%-8s %-8s %-8s %-18s %-22s %-15s
",$1,$2,$3,$4,$5,$6}'
netstat.txt Proto Recv-Q Send-Q Local-Address Foreign-Address State tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN tcp 0 0 coolshell.cn:80 124.205.5.146:18245 TIME_WAIT tcp 0 0 coolshell.cn:80 61.140.101.185:37538 FIN_WAIT2 tcp 0 0 coolshell.cn:80 110.194.134.189:1032 ESTABLISHED tcp 0 0 coolshell.cn:80 123.169.124.111:49809 ESTABLISHED tcp 0 0 coolshell.cn:80 116.234.127.77:11502 FIN_WAIT2 tcp 0 0 coolshell.cn:80 123.169.124.111:49829 ESTABLISHED tcp 0 0 coolshell.cn:80 183.60.215.36:36970 TIME_WAIT tcp 0 4166 coolshell.cn:80 61.148.242.38:30901 ESTABLISHED tcp 0 1 coolshell.cn:80 124.152.181.209:26825 FIN_WAIT1 tcp 0 0 coolshell.cn:80 110.194.134.189:4796 ESTABLISHED tcp 0 0 coolshell.cn:80 183.60.212.163:51082 TIME_WAIT tcp 0 1 coolshell.cn:80 208.115.113.92:50601 LAST_ACK tcp 0 0 coolshell.cn:80 123.169.124.111:49840 ESTABLISHED tcp 0 0 coolshell.cn:80 117.136.20.85:50025 FIN_WAIT2 tcp 0 0 :::22 :::* LISTEN

レコードをフィルタリングする方法を見てみましょう(次のフィルタ条件は、3番目のカラムの値が0&&6番目のカラムの値がLISTENです)
$ awk '$3==0 && $6=="LISTEN" ' netstat.txt
tcp        0      0 0.0.0.0:3306               0.0.0.0:*              LISTEN
tcp        0      0 0.0.0.0:80                 0.0.0.0:*              LISTEN
tcp        0      0 127.0.0.1:9000             0.0.0.0:*              LISTEN
tcp        0      0 :::22                      :::*                   LISTEN

「==」は比較演算子です.その他の比較演算子:!=,>,<,>=,<=記録をフィルタする方法を見てみましょう.
$ awk ' $3>0 {print $0}' netstat.txt
Proto Recv-Q Send-Q Local-Address          Foreign-Address             State
tcp        0   4166 coolshell.cn:80        61.148.242.38:30901         ESTABLISHED
tcp        0      1 coolshell.cn:80        124.152.181.209:26825       FIN_WAIT1
tcp        0      1 coolshell.cn:80        208.115.113.92:50601        LAST_ACK

ヘッダが必要であれば、組み込み変数NRを導入することができます.
$ awk '$3==0 && $6=="LISTEN" || NR==1 ' netstat.txt
Proto Recv-Q Send-Q Local-Address          Foreign-Address             State
tcp        0      0 0.0.0.0:3306           0.0.0.0:*                   LISTEN
tcp        0      0 0.0.0.0:80             0.0.0.0:*                   LISTEN
tcp        0      0 127.0.0.1:9000         0.0.0.0:*                   LISTEN
tcp        0      0 :::22                  :::*                        LISTEN

フォーマット出力を追加:
$ awk '$3==0 && $6=="LISTEN" || NR==1 {printf "%-20s %-20s %s
",$4,$5,$6}'
netstat.txt Local-Address Foreign-Address State 0.0.0.0:3306 0.0.0.0:* LISTEN 0.0.0.0:80 0.0.0.0:* LISTEN 127.0.0.1:9000 0.0.0.0:* LISTEN :::22 :::* LISTEN

組み込み変数では、組み込み変数について説明します.awkのいくつかの組み込み変数を見てみましょう.
変数#ヘンスウ#
意味
$0
現在のレコード(この変数にはロー全体が格納されています)
1  n
現在記録されているn番目のフィールドは、フィールド間がFSで区切られている
FS
入力フィールド区切り文字のデフォルトはスペースまたはTabです.
NF
現在のレコードのフィールドの数は、カラム数です.
NR
すでに読み出したレコード数は、行番号であり、1から、複数のファイルがあれば、この値もどんどん加算されます.
FNR
現在のレコード数は、NRとは異なり、この値は各ファイル独自の行番号になります.
RS
入力されたレコード区切り文字、デフォルトは改行文字
OFS
フィールド区切り文字を出力します.デフォルトもスペースです.
ORS
出力されたレコード区切り文字、デフォルトは改行文字
FILENAME
現在入力されているファイルの名前
行番号を出力するには、次のようにします.
$ awk '$3==0 && $6=="ESTABLISHED" || NR==1 {printf "%02s %s %-20s %-20s %s
",NR, FNR, $4,$5,$6}'
netstat.txt 01 1 Local-Address Foreign-Address State 07 7 coolshell.cn:80 110.194.134.189:1032 ESTABLISHED 08 8 coolshell.cn:80 123.169.124.111:49809 ESTABLISHED 10 10 coolshell.cn:80 123.169.124.111:49829 ESTABLISHED 14 14 coolshell.cn:80 110.194.134.189:4796 ESTABLISHED 17 17 coolshell.cn:80 123.169.124.111:49840 ESTABLISHED

区切り文字の指定
$  awk  'BEGIN{FS=":"} {print $1,$3,$6}' /etc/passwd
root 0 /root
bin 1 /bin
daemon 2 /sbin
adm 3 /var/adm
lp 4 /var/spool/lpd
sync 5 /sbin
shutdown 6 /sbin
halt 7 /sbin

上のコマンドも等価です:(-Fは区切り記号を指定することを意味します)
$ awk -F: '{print $1,$3,$6}' /etc/passwd

注意:複数の区切り文字を指定する場合は、次のようにします.
awk -F '[;:]'

さらに、tを区切り記号として出力する例を見てみましょう(以下、/etc/passwdファイルを使用します.このファイルは:区切ります):
$ awk  -F: '{print $1,$3,$6}' OFS="\t" /etc/passwd
root    0       /root
bin     1       /bin
daemon  2       /sbin
adm     3       /var/adm
lp      4       /var/spool/lpd
sync    5       /sbin

文字列マッチング文字列マッチングの例をいくつか見てみましょう.
$ awk '$6 ~ /FIN/ || NR==1 {print NR,$4,$5,$6}' OFS="\t" netstat.txt
1       Local-Address   Foreign-Address State
6       coolshell.cn:80 61.140.101.185:37538    FIN_WAIT2
9       coolshell.cn:80 116.234.127.77:11502    FIN_WAIT2
13      coolshell.cn:80 124.152.181.209:26825   FIN_WAIT1
18      coolshell.cn:80 117.136.20.85:50025     FIN_WAIT2

$ $ awk '$6 ~ /WAIT/ || NR==1 {print NR,$4,$5,$6}' OFS="\t" netstat.txt
1       Local-Address   Foreign-Address State
5       coolshell.cn:80 124.205.5.146:18245     TIME_WAIT
6       coolshell.cn:80 61.140.101.185:37538    FIN_WAIT2
9       coolshell.cn:80 116.234.127.77:11502    FIN_WAIT2
11      coolshell.cn:80 183.60.215.36:36970     TIME_WAIT
13      coolshell.cn:80 124.152.181.209:26825   FIN_WAIT1
15      coolshell.cn:80 183.60.212.163:51082    TIME_WAIT
18      coolshell.cn:80 117.136.20.85:50025     FIN_WAIT2

上記の第1の例はFIN状態に一致し、第2の例はWAIT文字の状態に一致する.実は~表示モードが始まります.//で指定します.これが正規表現の一致です.
実はawkはgrepのように最初の行に一致することができます.
$ awk '/LISTEN/' netstat.txt
tcp        0      0 0.0.0.0:3306            0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.1:9000          0.0.0.0:*               LISTEN
tcp        0      0 :::22                   :::*                    LISTEN

FINまたはTIMEに一致するには、「/FIN|TIME/」を使用します.
$ awk '$6 ~ /FIN|TIME/ || NR==1 {print NR,$4,$5,$6}' OFS="\t" netstat.txt
1       Local-Address   Foreign-Address State
5       coolshell.cn:80 124.205.5.146:18245     TIME_WAIT
6       coolshell.cn:80 61.140.101.185:37538    FIN_WAIT2
9       coolshell.cn:80 116.234.127.77:11502    FIN_WAIT2
11      coolshell.cn:80 183.60.215.36:36970     TIME_WAIT
13      coolshell.cn:80 124.152.181.209:26825   FIN_WAIT1
15      coolshell.cn:80 183.60.212.163:51082    TIME_WAIT
18      coolshell.cn:80 117.136.20.85:50025     FIN_WAIT2

パターンの反転の例を見てみましょう.
$ awk '$6 !~ /WAIT/ || NR==1 {print NR,$4,$5,$6}' OFS="\t" netstat.txt 1 Local-Address Foreign-Address State 2 0.0.0.0:3306    0.0.0.0:*       LISTEN
3       0.0.0.0:80      0.0.0.0:*       LISTEN
4       127.0.0.1:9000  0.0.0.0:*       LISTEN
7       coolshell.cn:80 110.194.134.189:1032    ESTABLISHED
8       coolshell.cn:80 123.169.124.111:49809   ESTABLISHED
10      coolshell.cn:80 123.169.124.111:49829   ESTABLISHED
12      coolshell.cn:80 61.148.242.38:30901     ESTABLISHED
14      coolshell.cn:80 110.194.134.189:4796    ESTABLISHED
16      coolshell.cn:80 208.115.113.92:50601    LAST_ACK
17      coolshell.cn:80 123.169.124.111:49840   ESTABLISHED
19      :::22   :::*    LISTEN

または、
awk '!/WAIT/' netstat.txt

折りたたみファイルawkでファイルを分割するのは簡単で、リダイレクトを使えばいいです.次の例は、6例目でファイルを区切ったもので、かなり簡単です(NR!=1はテーブルヘッダを処理しないことを示します).
$ awk 'NR!=1{print > $6}' netstat.txt $ ls ESTABLISHED FIN_WAIT1 FIN_WAIT2 LAST_ACK LISTEN netstat.txt TIME_WAIT $ cat ESTABLISHED tcp 0 0 coolshell.cn:80        110.194.134.189:1032        ESTABLISHED
tcp        0      0 coolshell.cn:80        123.169.124.111:49809       ESTABLISHED
tcp        0      0 coolshell.cn:80        123.169.124.111:49829       ESTABLISHED
tcp        0   4166 coolshell.cn:80        61.148.242.38:30901         ESTABLISHED
tcp        0      0 coolshell.cn:80        110.194.134.189:4796        ESTABLISHED
tcp        0      0 coolshell.cn:80        123.169.124.111:49840       ESTABLISHED

$ cat FIN_WAIT1
tcp        0      1 coolshell.cn:80        124.152.181.209:26825       FIN_WAIT1

$ cat FIN_WAIT2
tcp        0      0 coolshell.cn:80        61.140.101.185:37538        FIN_WAIT2
tcp        0      0 coolshell.cn:80        116.234.127.77:11502        FIN_WAIT2
tcp        0      0 coolshell.cn:80        117.136.20.85:50025         FIN_WAIT2

$ cat LAST_ACK
tcp        0      1 coolshell.cn:80        208.115.113.92:50601        LAST_ACK

$ cat LISTEN
tcp        0      0 0.0.0.0:3306           0.0.0.0:*                   LISTEN
tcp        0      0 0.0.0.0:80             0.0.0.0:*                   LISTEN
tcp        0      0 127.0.0.1:9000         0.0.0.0:*                   LISTEN
tcp        0      0 :::22                  :::*                        LISTEN

$ cat TIME_WAIT
tcp        0      0 coolshell.cn:80        124.205.5.146:18245         TIME_WAIT
tcp        0      0 coolshell.cn:80        183.60.215.36:36970         TIME_WAIT
tcp        0      0 coolshell.cn:80        183.60.212.163:51082        TIME_WAIT

指定した列をファイルに出力することもできます.
awk 'NR!=1{print $4,$5 > $6}' netstat.txt

もっと複雑です:(if-else-if文に注意してください.awkはスクリプト解釈器であることがわかります)
$ awk 'NR!=1{if($6 ~ /TIME|ESTABLISHED/) print > "1.txt"; else if($6 ~ /LISTEN/) print > "2.txt"; else print > "3.txt" }' netstat.txt

$ ls ?.txt
1.txt  2.txt  3.txt

$ cat 1.txt
tcp        0      0 coolshell.cn:80        124.205.5.146:18245         TIME_WAIT
tcp        0      0 coolshell.cn:80        110.194.134.189:1032        ESTABLISHED
tcp        0      0 coolshell.cn:80        123.169.124.111:49809       ESTABLISHED
tcp        0      0 coolshell.cn:80        123.169.124.111:49829       ESTABLISHED
tcp        0      0 coolshell.cn:80        183.60.215.36:36970         TIME_WAIT
tcp        0   4166 coolshell.cn:80        61.148.242.38:30901         ESTABLISHED
tcp        0      0 coolshell.cn:80        110.194.134.189:4796        ESTABLISHED
tcp        0      0 coolshell.cn:80        183.60.212.163:51082        TIME_WAIT
tcp        0      0 coolshell.cn:80        123.169.124.111:49840       ESTABLISHED

$ cat 2.txt
tcp        0      0 0.0.0.0:3306           0.0.0.0:*                   LISTEN
tcp        0      0 0.0.0.0:80             0.0.0.0:*                   LISTEN
tcp        0      0 127.0.0.1:9000         0.0.0.0:*                   LISTEN
tcp        0      0 :::22                  :::*                        LISTEN

$ cat 3.txt
tcp        0      0 coolshell.cn:80        61.140.101.185:37538        FIN_WAIT2
tcp        0      0 coolshell.cn:80        116.234.127.77:11502        FIN_WAIT2
tcp        0      1 coolshell.cn:80        124.152.181.209:26825       FIN_WAIT1
tcp        0      1 coolshell.cn:80        208.115.113.92:50601        LAST_ACK
tcp        0      0 coolshell.cn:80        117.136.20.85:50025         FIN_WAIT2

以下のコマンドを統計して、すべてのCファイル、CPPファイル、Hファイルのファイルサイズの合計を計算します.
$ ls -l  *.cpp *.c *.h | awk '{sum+=$5} END {print sum}'
2511401

各connection状態を統計する方法を見てみましょう.
$ awk 'NR!=1{a[$6]++;} END {for (i in a) print i ", " a[i];}' netstat.txt
TIME_WAIT, 3
FIN_WAIT1, 1
ESTABLISHED, 6
FIN_WAIT2, 3
LAST_ACK, 1
LISTEN, 4

各ユーザのプロセスがどれだけのメモリを占めているかを統計する(注:sumのRSSの列
$ ps aux | awk 'NR!=1{a[$1]+=$6;} END { for(i in a) print i ", " a[i]"KB";}'
dbus, 540KB
mysql, 99928KB
www, 3264924KB
root, 63644KB
hchen, 6020KB

awkスクリプトにはENDキーワードが表示されます.ENDは「すべての行の表示を処理した」という意味で、ENDといえばBEGINを紹介する必要があります.この2つのキーワードは実行前と実行後の意味を意味します.BEGINには、実行前の文が入っています.ENDには、すべての行を処理した後に実行する文が入っています.この中には、各行を処理するときに実行する文が入っています.このことを明らかにするために、次の例を見てみましょう.
このような書類(学生成績表)があるとします.
$ cat score.txt
Marry   2143 78 84 77
Jack    2321 66 78 45
Tom     2122 48 77 71
Mike    2537 87 97 95
Bob     2415 40 57 62

私たちのawkスクリプトは以下の通りです(コマンドラインに書いていないのは、コマンドラインが読みにくいためです.また、別の使い方も紹介しています).
$ cat cal.awk
#!/bin/awk -f
#   
BEGIN {
    math = 0
    english = 0
    computer = 0

    printf "NAME NO. MATH ENGLISH COMPUTER TOTAL
"
printf "---------------------------------------------
"
} # { math+=$3 english+=$4 computer+=$5 printf "%-6s %-6s %4d %8d %8d %8d
"
, $1, $2, $3,$4,$5, $3+$4+$5 } # END { printf "---------------------------------------------
"
printf " TOTAL:%10d %8d %8d
"
, math, english, computer printf "AVERAGE:%10.2f %8.2f %8.2f
"
, math/NR, english/NR, computer/NR }

実行結果を見てみましょう:(このように./cal.awk score.txtを実行することもできます)
$ awk -f cal.awk score.txt
NAME NO. MATH ENGLISH COMPUTER TOTAL ---------------------------------------------
Marry  2143     78       84       77      239
Jack   2321     66       78       45      189
Tom    2122     48       77       71      196
Mike   2537     87       97       95      279
Bob 2415 40 57 62 159 ---------------------------------------------
 TOTAL: 319 393 350
AVERAGE:     63.80    78.60    70.00

環境変数スクリプトといえば、環境変数とどのように対話するかを見てみましょう:(-vパラメータとENVIRONを使用し、ENVIRONを使用する環境変数にはexportが必要です)
$ x=5

$ y=10
$ export y

$ echo $x $y
5 10

$ awk -v val=$x '{print $1, $2, $3, $4+val, $5+ENVIRON["y"]}' OFS="\t" score.txt
Marry   2143    78      89      87
Jack    2321    66      83      55
Tom     2122    48      82      81
Mike    2537    87      102     105
Bob     2415    40      62      72

小例
 file         80  
awk 'length>80' file

         IP
netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr

  99   
seq 9 | sed 'H;g' | awk -v RS='' '{for(i=1;i<=NF;i++)printf("%dx%d=%d%s", i, NR, i*NR, i==NR?"
":"\t")}'

組み込み変数を参照してください.http://www.gnu.org/software/gawk/manual/gawk.html#Built_002din-Variablesのフローコントロールについては、以下を参照してください.http://www.gnu.org/software/gawk/manual/gawk.html#Statements組み込み関数、参照:http://www.gnu.org/software/gawk/manual/gawk.html#Built_002din正規表現、参照:http://www.gnu.org/software/gawk/manual/gawk.html#Regexp