awkを学ぶ

# awkって何？
AWK(オーク)

- データの操作やテキスト処理に使われるスクリプト言語およびコマンドラインツールです
- Unix系OSやLinux環境でよく使われ、テキストの中から特定のパターンを検索・抽出し、それに対して様々な処理を行うために使用されます

# awkの基本


## 構文
```awk
awk オプション '条件 { action }' input_file > output_file
```
:::note info
- `条件`がない場合は全てのパターンでアクションが行われる
- 出力する必要がなければ`> 出力ファイル`は必要ない
:::


## 特殊変数
特定の行やフィールドを表す特殊な変数があります。

- **フィールド:** デフォルトでは空白やタブで区切られ、列のことを指します

| 変数 | 説明 |
|:-:|:-:|
| `NR` | 現在の行番号を表します。1から始まり、ファイルの行数に応じて増加します。 |
| `NF` | 現在の行のフィールド数を表します。 |
| `$0` | 現在の行全体を表します。 |
| `$1, $2, ...` | 各フィールドを表します。例えば、`$1`は1列目、`$2`は2列目を表します。 |

## 正規表現
1. スラッシュで正規表現を囲む
    ```awk
    awk '/pattern/ { print $0 }'
    ```
    
2. 正規表現演算子
    - `~`: 左辺の値が右辺の正規表現にマッチする場合
    - `!~`:  左辺の値が右辺の正規表現にマッチしない場合
    ```awk
    # 正規表現にマッチする場合
    awk '$1 ~ /pattern/ { print $0 }'
    
    # 正規表現にマッチしない場合
    awk '$1 !~ /pattern/ { print $0 }'
    ```
3. メタ文字
    | メタ文字 | 説明 | 例 |
    |:-:|:-:|:-:|
    | `.` | 任意の１文字に一致 | `a.b` → `aab`, `acb`, `a1b` など |
    | `*` | 直前の文字が0回以上繰り返される部分に一致 | a* → 空文字列、a, aa, aaa |
    | `+` | 直前の文字が1回以上繰り返される部分に一致 | `a+` → `a`, `aa`, `aaa` |
    | `?` | 直前の文字が0回または1回出現する部分に一致 | `a?` → 空文字列、`a` |
    | `^` | 行の先頭に一致 | `^a` → `a` で始まる行 |
    | `$` | 行の末尾に一致 | `a$` → `a` で終わる行 |
    | `[ ]` | 文字クラス．中のいずれか1文字に一致 | `[abc]` → `a`, `b`, `c` |
    | `[^ ]` | 否定の文字クラス．中の任意の文字以外に一致 | `[^abc]` → `a`, `b`, `c` 以外 |
    | `\` | メタ文字をエスケープ | `\.` → ピリオドそのもの |
    | `{n}` | 直前の文字がちょうど n 回出現する部分に一致 | `a{2}` → `aa` |
    | `{n,}` | 直前の文字が n 回以上繰り返される部分に一致 | `a{2,}` → `aa`, `aaa`, `aaaa` |
    | `{n,m}` | 直前の文字が n 回以上 m 回以下繰り返される部分に一致 | `a{2,3}` → `aa`, `aaa` |
    | `\|` | OR（いずれかに一致） | `a\|b` → `a`, `b` |
    | `()` | グループ化 | `(ab)+` → `ab`, `abab`, `ababab` |

    :::note info
    `Makefile`などで使用する際は、Makefileの変数参照である`$`と正規表現の`$`やawkの特殊変数である`$0`などの **`$`** が被っている。そのためMakefile内では`$`は`$$`として使用される。

# オプション
`-f <file>`
- 説明: 指定したファイルを awk スクリプトとして実行
- 使用例: `myscript.awk`というファイル内の`awk`スクリプトを`input.txt`に対して実行
    ```awk
    awk -f myscript.awk input.txt
    ```

\
`-v <var=value>`
- 説明: スクリプト内で使用する変数をコマンドラインから指定
- 使用例: 一列目が`hello`の時、その行を出力する
    ```awk
    awk -v var="hello" '$1 ~ var { print $0 }' input.txt
    ```

\
`-F <fieldsep>`
- 説明: フィールドの区切り文字を指定
- 使用例: カンマ区切りのファイル`input.csv`の1列目を出力
    ```awk
    awk -F "," '{ print $1 }' input.csv
    ```

# 条件
AWKでは、条件を指定して特定の行に対してアクションを実行できます。
条件には、
- 比較演算子（==, !=, <, >, <=, >=）
- 論理演算子（&&, ||, !）
- パターンマッチ（正規表現）
などがあります。


#アクション
以下のように条件を指定し、複数のアクションを処理する形になります。

```
awk '条件 { action1; action2; }'
```

## awkの関数
### 1. 標準出力とシステムコマンド
`print`
- 説明: `awk`で標準出力にデータを表示する

:::note info
`,`で区切ると、スペース区切りで表示される。
:::

- 使用例: 
    ```awk
    echo "apple banana cherry" | awk '{ print $1, $2, "is tasty" }'
    ```

\
`printf`
- 使用例: 
    ```awk
    echo "apple banana cherry" | awk '{ printf "%s %s\n", $1, $2 }'
    ```
:::note info
- %s は文字列として出力
- %d は整数を出力
:::

\
`system`
- 説明: システムコマンドを実行するための関数
- 使用例: helloディレクトリが作成される
    ```awk
    echo "hello" |awk 'BEGIN { system("mkdir" $1) }'
    ```

### 2. 文字列操作

`length`
- 説明: 文字列または行全体の長さを返す
- 使用例: 現在の行の長さを返す
    ```awk
    awk '{ print length($0) }' input.txt
    ```
    
\
`substr`
- 説明: 文字列内の一部分を抽出する
- 構文:
    ```awk
    substr(string, start [, length])
    ```
- 使用例:一列目の１〜３文字目を抽出
    ```awk
    awk '{ print substr($1, 1, 3) }' input.txt
    ```

\
`index`
- 説明: 文字列内の部分文字列が最初に現れる位置を返す
- 使用例: 文字列aが一番最初に現れる位置
    ```awk
    awk '{ print index($1, "a") }' input.txt
    ```

### 3. 数値演算関数
`int`
- 説明: 実数を整数に変換（小数点以下を切り捨て）
- 使用例: 
    ```awk
    awk '{ print int($1) }' input.txt
    ```

\
`sqrt`
- 説明: 数値の平方根を計算
- 使用例: 
    ```awk
    awk '{ print sqrt($1) }' input.txt
    ```

### 4. 置換関数
`tolower` / `toupper`
- 説明: 文字列を小文字または大文字に変換
- 使用例: 一列目を小文字に変換
    ```awk
    awk '{ print tolower($1) }' input.txt
    ```

\
`sub`
- 説明: 文字列の最初にマッチする部分を置き換える
- 構文: 
    ```awk
    sub(regexp, replacement, target)
    ```
    - regexp: 置き換え対象の正規表現
    - replacement: 置き換える文字列
    - target: 置き換えを行う対象の文字列（省略可能で、省略した場合は $0 が使われます）
- 使用例: bananaをorangeに
    ```awk
    echo "apple banana cherry" | awk '{ sub("banana", "orange"); print }'
    ```
    - 出力: 
        ```awk
        apple orange cherry
        ```

\
`gsub`
- 説明: 文字列内のすべてのマッチを置き換える
- 構文: 
    ```awk
    gsub(regexp, replacement, target)
    ```
    - regexp: 置き換え対象の正規表現
    - replacement: 置き換える文字列
    - target: 置き換えを行う対象の文字列（省略可能で、省略した場合は $0 が使われます）
- 使用例: 全てのaをoに
    ```awk
    echo "banana apple" | awk '{ gsub("a", "o"); print }'
    ```
    - 出力: 
        ```awk
        bonono opple
        ```

## 5. 制御構文
AWKでは、次の制御構造を使用して、条件分岐や繰り返し処理を行うことができる。

- if文: 
    ```awk
    if (条件) {
        アクション
    }
    ```

- for:
    ```awk
    for (初期化; 条件; 更新) {
        アクション
    }
    ```

- while:
    ```awk
    while (条件) {
        アクション
    }
    ```

## 6. BEGIN と END
`BEGIN`
- 説明: `awk`プログラムの最初に実行されるコードブロックであり、初期化や設定を行う
- 使用例: 
    - 初期化処理
    - ヘッダー出力
    - 変数の設定
    - 開始メッセージの表示
    
    input.txt からデータを読み込む前に "Name" と "Age" のヘッダーを出力
    ```awk
    awk 'BEGIN { print "Name\tAge" } { print $1 "\t" $2 }' input.txt
    ```

`END`
- 説明: awk プログラムの最後に実行されるコードブロックであり、すべてのデータが処理された後に、結果の出力や後処理を行う
- 使用例: 
    - 合計や平均の出力
    - 結果の整理
    - 終了メッセージの表示
    ```awk
    awk '{ sum += $1; count += 1; } END { print "AVERAGE:", sum / count }' input.txt
    ```

# まとめ
AWKは、テキスト処理に非常に便利なものです。特定のパターンに基づいてデータをフィルタリングしたり、条件に応じて異なる処理をすることが可能です。今回の説明ではAWKの標準的な機能の紹介になります。AWKを拡張したgawkなどもあり、今後使用する機会があれば、追記しようと思います。