awkコマンドの使い方

#1 awkコマンドとは
空白やカンマなどで区切られたファイルを扱うコマンドです。

#2 環境
VMware Workstation 15 Player上の仮想マシンを使いました。
仮想マシンの版数は以下のとりです。

```console:CentOSの版数
[root@server ~]# cat /etc/redhat-release
CentOS Linux release 8.3.2011
```

```console:カーネル版数
[root@server ~]# uname -r
4.18.0-240.el8.x86_64
```

```console:awkの版数
[root@server ~]# awk -V
GNU Awk 4.2.1, API: 2.0 (GNU MPFR 3.1.6-p2, GNU MP 6.1.2)
Copyright (C) 1989, 1991-2018 Free Software Foundation.
-snip-
```

#3 awkコマンドの書式
`awk [オプション] [パターン] [アクション] [ファイル]`


#4 区切り文字の使い方(-F)

##4.1 スペースを区切り文字として使う方法
スペースで区切られたファイルを作成します。

```console:テスト用ファイル作成
[root@server awk]# vi test.txt
[root@server awk]# cat test.txt
111 222 333
```

1列目のフィールドを表示してみます。

```console:実行結果
[root@server awk]# cat test.txt | awk '{print $1}'
111
```

フィールドの表示順序を変えてみます。
3列目->2列目->1列目の順番で表示してみます。

```console:実行結果
[root@server awk]# cat test.txt | awk '{print $3,$2,$1}'
333 222 111
```

区切り文字としてスペースを明示的に指定する場合は、以下のように実行します。

```console:実行結果（明示的に区切り文字を指定する場合）
[root@server awk]# cat test.txt | awk -F " " '{print $2,$3}'
222 333
```

##4.2 カンマを区切り文字として使う方法
カンマで区切られたファイルを作成します。

```console:テスト用ファイル作成
[root@server awk]# vi test.txt
[root@server awk]# cat test.txt
111,222,333
```

```console:実行結果
[root@server awk]# cat test.txt | awk -F "," '{print $2}'
222
```


##4.3 区切り文字を複数指定する方法(`[]`)
カンマとコロンで区切られたファイルを作成します。

```console:テスト用ファイル作成
[root@server awk]# cat test.txt
111,222:333
```

区切り文字を複数指定する場合は、`[]`の中に区切り文字を指定します。

```console:実行結果
[root@server awk]# cat test.txt | awk -F "[,:]" '{print $1}'
111
[root@server awk]# cat test.txt | awk -F "[,:]" '{print $2}'
222
[root@server awk]# cat test.txt | awk -F "[,:]" '{print $3}'
333
```

##4.4 タブを区切り文字として使う方法(`\t`)
タブで区切られたファイルを作成します。

```console:テスト用ファイル作成
[root@server awk]# cat test.txt
111     222     333
```

```console:実行結果
[root@server awk]# cat test.txt | awk -F "\t" '{print $2}'
222
```

#5 組み込み変数の使い方

| 変数名 | 意味 |
|:-----|:---------------------------|
| NR   |現在処理しているレコードの行番号 |
| NF  | レコード中のフィールド数  |
| $0  | レコード  |
| $n  | レコードのn番目のフィールド  |
|OFS|フィールドの区切り（デフォルトは空白） |
|FILENAME |ファイル名  |


##5.1 NR,$0,$nの使い方

```console:テスト用ファイル作成
[root@server awk]# cat test.txt
111 aaa
222 bbb
333 ccc
```

処理中のレコードの行番号とレコードを表示してみます。

```console:実行結果
[root@server awk]# cat test.txt | awk '{print NR,$0}'
1 111 aaa
2 222 bbb
3 333 ccc
```

2行目のレコードを表示してみます。

```console:実行結果
[root@server awk]# cat test.txt | awk 'NR==2 {print $0}'
222 bbb
```

3行目のレコードの2番目のフィールドを表示してみます。

```console:実行結果
[root@server awk]# cat test.txt | awk 'NR==3 {print $2}'
ccc
```

##5.2 NFの使い方

```console:テスト用ファイル作成
[root@server awk]# cat test.txt
aaa
bbb ccc
ddd eee fff
```

左端がフィールド数を表しています。
1行目は1個、2行目は2個、3行目は3個であることがわかります。

```console:実行結果
[root@server awk]# cat test.txt | awk '{print NF,$0}'
1 aaa
2 bbb ccc
3 ddd eee fff
```

##5.3 OFSの使い方

```console:テスト用ファイル作成
[root@server awk]# cat test.txt
111 aaa
222 bbb
333 ccc
```

フィールドの区切り文字をコロンで出力してみます。

```console:実行結果
[root@server awk]# cat test.txt | awk 'OFS = ":" {print $1,$2}'
111:aaa
222:bbb
333:ccc
```

##5.4 FILENAME の使い方
ファイル名に続いて、各行のレコードを表示してみます。

```console:実行結果
[root@server awk]# awk '{print FILENAME,$0}' test.txt
test.txt 111 aaa
test.txt 222 bbb
test.txt 333 ccc
```

#6 正規表現の使い方
##6.1 行頭(`^`)

```console:テスト用ファイル作成
[root@server awk]# cat test.txt
111 aaaa
222 abcd
333 AAAA
```

行頭が`1`ではじまる行を表示してみます。

```console:実行結果
[root@server awk]# cat test.txt |awk '/^1/ {print $0}'
111 aaaa
```

##6.2 行末(`$`)

行末が`A`で終わる行を表示してみます。

```console:実行結果
[root@server awk]# cat test.txt |awk '/A$/ {print $0}'
333 AAAA
```

##6.3 語頭(`\<`)
`aa`で始まる単語が含まれる行を検索してみます。

```console:実行結果
[root@server awk]# cat test.txt | awk '/\<aa/ {print $0}'
111 aaaa
```

##6.4 語尾(`\>`)
`AA`で終わる単語が含まれる行を検索してみます。

```console:実行結果
[root@server awk]# cat test.txt | awk '/\<AA/ {print $0}'
333 AAAA
```

##6.5 マッチする行を抜き出す方法
`bc`を含む行を検索してみます。

```console:実行結果
[root@server awk]# cat test.txt | awk '/bc/ { print $0 }'
222 abcd
```

##6.6 マッチしない行を抜き出す方法('!')
`bc`を含まない行を検索してみます。

```console:実行結果
[root@server awk]# cat test.txt | awk '!/bc/ { print $0 }'
111 aaaa
333 AAAA
```

##6.7 パターン1からパターン2にマッチするまでアクションを実行する方法(`/パターン1/,/パターン2/`)
`111`ではじまって、`222`が出現するまでレコードを表示してみます。

```console:実行結果
[root@server awk]# cat test.txt |awk '/111/,/222/ { print $0 }'
111 aaaa
222 abcd
```

#7 実践編
##7.1 デフォルトゲートウェイを取得する方法

```console:ルーティングテーブルの確認
[root@server ~]# ip route
default via 192.168.2.1 dev ens33 proto static metric 100
192.168.2.0/24 dev ens33 proto kernel scope link src 192.168.2.115 metric 100
192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1 linkdown
```

```console:実行結果
[root@server ~]# ip route | awk '$1=="default" {print $3}'
[root@server ~]# 192.168.2.1
```

##7.2 ログファイルから特定日時のメッセージを抽出する方法
まずは、抽出元のログファイルを確認します。
日時が`Apr 18 20:20:18`から`Apr 18 20:26:33`にマッチするメッセージは
`IPv6: MLD:...`になります。

```console:ログファイルの確認
[root@server ~]# cat  /var/log/messages|head -n 10
Apr 18 20:20:02 server systemd[1]: httpd.service: Unit cannot be reloaded because it is inactive.
Apr 18 20:20:02 server rsyslogd[1249]: [origin software="rsyslogd" swVersion="8.1911.0-6.el8" x-pid="1249" x-info="https://www.rsyslog.com"] rsyslogd was HUPed
Apr 18 20:20:18 server kernel: IPv6: MLD: clamping QRV from 1 to 2!
Apr 18 20:22:23 server kernel: IPv6: MLD: clamping QRV from 1 to 2!
Apr 18 20:24:28 server kernel: IPv6: MLD: clamping QRV from 1 to 2!
Apr 18 20:26:33 server kernel: IPv6: MLD: clamping QRV from 1 to 2!
Apr 18 20:28:31 server systemd[1]: Created slice system-user\x2druntime\x2ddir.slice.
Apr 18 20:28:31 server systemd[1]: Created slice User Slice of UID 0.
Apr 18 20:28:31 server systemd[1]: Started /run/user/0 mount wrapper.
Apr 18 20:28:31 server systemd[1]: Starting User Manager for UID 0...
```


```console:実行結果
[root@server ~]# cat /var/log/messages|awk '/Apr 18 20:20:18/,/Apr 18 20:26:33/ { print $0 }'
Apr 18 20:20:18 server kernel: IPv6: MLD: clamping QRV from 1 to 2!
Apr 18 20:22:23 server kernel: IPv6: MLD: clamping QRV from 1 to 2!
Apr 18 20:24:28 server kernel: IPv6: MLD: clamping QRV from 1 to 2!
Apr 18 20:26:33 server kernel: IPv6: MLD: clamping QRV from 1 to 2!
```

##7.3 特定のファイルサイズを抽出する方法
/bin配下のファイルサイズが30000000(byte)以上のファイルを抽出してみます。
`$5`は`ls -l`を実行したときの5番目のフィールド（ファイルサイズ）を表します。

```console:実行結果
[root@server awk]# ls -l /bin/ | awk '$5>=30000000 {print $0}'
-rwxr-xr-x. 1 root root      30057712  2月 22 13:47 buildah
-rwxr-xr-x. 1 root root      53912832  2月 22 13:53 podman
```
なお、同様のことはfindコマンドを使ってもできます。
findコマンドは、[findコマンドの使い方](https://qiita.com/hana_shin/items/d1b420568d3c2223bd07#6-%E3%82%B5%E3%82%A4%E3%82%BA%E3%81%A7%E6%A4%9C%E7%B4%A2%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95-size)を参照してください。


#Z 参考情報
[awkの-Fオプションで区切り文字を指定する方法](https://it-ojisan.tokyo/awk-f/)
[【 awk 】コマンド（基本編その4）――テキストの加工とパターン処理、printとprintfの使い方](https://www.atmarkit.co.jp/ait/articles/1706/15/news021.html)
[【 awk 】コマンド（応用編その6）――テキストの加工とパターン処理、複数ファイルの処理](https://www.atmarkit.co.jp/ait/articles/1806/01/news041.html)
[awk コマンド](https://hydrocul.github.io/wiki/commands/awk.html)
[AWKで一番の得意ワザ！ シェルで文字列を自在に扱うための文字列関数](https://codezine.jp/article/detail/7912)
[awkを使って特定の日時の範囲でログを抽出する](https://mk-55.hatenablog.com/entry/2017/09/04/002549)