AWKのすすめ

# AWKとは
- 1977年に開発されたプログラミング言語。

- 「オーク」と読みます。

- 名前について
以下の開発者３名の頭文字を取っている。
・Alfred Vaino Aho
・Peter Jay Weinberger
・Brian Wilson Kernighan

- CUIでサクッとテキスト処理ができる。

- Linuxにデフォルトで入っている。

:::note info
この記事はワンライナーでawkを使用することを想定して記述しています。
:::

# 基本的な書き方
```awk
# パターンが真であればアクションが実行される
awk 'パターン {アクション}'

# アクションを省略した場合、パターンが真であれば{print $0}が実行される
awk 'パターン'

# パターンを省略した場合、全てのレコードに対してアクションが実行される
awk '{アクション}'
```

# 文字列出力
```awk
# "Hello World"を出力
echo | awk '{print "Hello World"}'
Hello World

# 文字列連結（各行に" foo"を連結して出力）
seq 3 | awk '{print $0 " foo"}'
1 foo
2 foo
3 foo
```

:::note warn
awkは何らかの入力を必要とします。
:::

# 算術演算子
|　記号　|　意味　|
|:---:|:---:|
|　+　|　加算　|
|　-　|　減算　|
|　*　|　乗算　|
|　/　|　除算　|
|　%　|　剰余　|
|　^　|　べき乗　|

# 比較演算子
|　記号　|　意味　|
|:---:|:---|
|　x < y　|　xがyより小さい　|
|　x > y　|　xがyより大きい　|
|　x <= y　|　xがy以下　|
|　x >= y　|　xがy以上　|
|　x == y　|　xとyが等しい　|
|　x != y　|　xとyが等しくない　|
|　x ~ /y/　|　xが正規表現yに適合する　|
|　x !~ /y/　|　xが正規表現に適合しない　|

# 論理演算子
|　記号　|　意味　|
|:---:|:---|
|　x && y　|　xかつy　|
|　x \|\| y　|　xまたはy　|
|　!x　|　xの否定　|

# 真偽

- 偽
  - 数字の`0`
  - 空の文字列`""`
- 真
  - 上記以外全て

```awk
# 偽（数字の0）
awk '0'

# 偽（空の文字列）
awk '""'

# 真（数字の1）
awk '1'

# 真（文字列の0）
awk '"0"'
```

真偽の知識があれば以下のようなコードも理解できるようになると思います。
```awk
# 偽
echo '0 1 2' | awk '$0 = $1'

# 真
echo '0 1 2' | awk '{print $1}'
0

# 真
echo '0 1 2' | awk '$0 = $2'
1
```

# 組み込み変数
とりあえずこれだけ知っておいたら良い組み込み変数です。

**$0**
　現在の入力レコード全体。

**$n**
　現在の入力レコードのn列目。（nは1以上）

**FS**
　*The input field separator.*
　区切り文字を定義する変数。
　デフォルトは`" "`
　`-F`オプションで変更可能

**NF**
　*The number of fields.*
　レコードのフィールド数が格納されている変数。

**NR**
　*The total number of input records.*
　これまでに読み込んだレコード数が格納されている変数。

**OFS**
　*The output field separator.*
　出力フィールドの区切り文字を定義する変数。
　デフォルトは`" "`

## NFの使用例
```awk
# 最後のフィールドを出力
awk '{print $NF}'

# 最後から１つ前のフィールドを出力
awk '{print $(NF-1)}'

# フィールド数が10以上を出力
awk 'NF >= 10'

# 空行を除いて出力（空行はNFが0になる為、出力されない）
awk 'NF'
```

## NRの使用例
```awk
# 3行目だけ出力
awk 'NR == 3'

# 10行目まで出力
awk 'NR <= 10'

# 5行目と10行目を出力
awk 'NR == 5 || NR == 10'

# 5行目と10行目以外を出力
awk 'NR != 5 || NR != 10'

# 5行目から10行目まで出力
awk 'NR == 5, NR == 10'

# 5行目から10行目まで出力
awk 'NR >= 5 && NR <= 10'

# 1行目から次の空行まで出力
awk 'NR == 1, /^$/'

# 最初の空行から後を出力
awk '/^$/, 0'

# 行数を出力
awk 'END{print NR}'

# 最後の行を出力
awk'END{print}'
```

# print文で使用する`,`について
print文で使う`,`はOFSに置き換えられる。

:::note info
OFSのデフォルトはスペース" "
:::

```awk
# 1列目と３列目と5列目を出力（スペース区切り）
echo 'a-b-c-d-e' | awk -F- '{print $1 $3 $5}'
ace

# 1列目と３列目と5列目を出力（,区切り）
echo 'a-b-c-d-e' | awk -F- '{print $1, $3, $5}'
a c e # 間に" "が入る

# /区切りで出力したい場合はOFSを変更する
echo 'a-b-c-d-e' | awk -F- '{OFS ="/"; print $1, $3, $5}'
a/c/e
```

# cutコマンドとの違い
cutコマンドとの大きな違いは区切り文字の指定に正規表現が使えるかどうかです。

```awk
# awkで３列目を出力
echo 'a b c d e' | awk '{print $3}'
c

# cutで３列目を出力
echo 'a b c d e' | cut -d" " -f3
c

# awkで３列目を出力
echo 'a b,c:d/e' | awk -F"[ ,:/]" '{print $3}'
c

# cutで３列目を出力（できない）
echo 'a b,c:d/e' | cut -d -f3 # cutでは区切り文字を複数指定できない 
```

# LinuxでのAWK使用例
こんな感じで使えます。

```awk
# ls -lコマンドから所有ユーザがrootのものを出力
ls -l | awk '$3 == "root"'

# ls -lコマンドから所有ユーザがrootでかつ所有グループがrootではないものを出力
ls -l | awk '$3 == "root" && $4 != "root"'

# ls -lコマンドからファイルサイズが1MB以上のものを出力
ls -l | awk '$5 >= 10^6'

# ls -lコマンドからファイルサイズの合計を出力
ls -l | awk '{sum += $5}; END{print sum}'

# /etc/passwdファイルからユーザIDが3桁のユーザ名を出力
cat /etc/passwd | awk -F: '$3 ~ /^[0-9]{3}$/{print $1}'
```

# 最後に
AWKは使っていてとても便利だと思います。
使ったことがない人はこの記事を読んだらぜひ使ってみてほしいです。

# 参考文献
この記事は以下の情報を参考にして執筆しました。
- awkコマンド マニュアル
- 書籍「プログラミング言語AWK」USP研究所
- 書籍「AWK実践入門」技術評論社
- 書籍「「シェル芸」に効く! AWK処方箋」翔泳社