サッと使いたいawkコマンド改めqコマンド

# 始めに
今まで数GBのようなある程度大きいCSVを処理したい時には、awkを使うことがそれなりにあった
理想的には、スプレッドシートで済ましたいけど、結構重くなって、場合によっては処理できないこともある

なので、頻度はそこまで多くないけど、必要な時にサッと使いたい機能をメモしておこう

```bash
# 例. 空文字でない該当列の値を出力
cat file.csv | awk 'BEGIN{FS=","} {if($6!=""&&$7!=""){print $6,$7}}' | less
```

と思ったのだけど、[awkは複雑なCSVを厳密にパースするのには向いていない](https://codezine.jp/article/detail/8323#:~:text=gawk%E3%81%AE%E9%96%8B%E7%99%BA%E3%81%AF%E3%80%8C%E5%AE%8C%E5%85%A8%E3%82%92%E7%9B%AE%E6%8C%87%E3%81%99%E3%81%AE%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%8F9%E5%89%B2%E3%82%92%E3%82%B5%E3%82%AF%E3%82%B5%E3%82%AF%E3%81%93%E3%81%AA%E3%81%97%E3%80%81%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E3%81%AB%E6%94%B9%E8%A1%8C%E3%82%92%E5%90%AB%E3%82%80%E3%82%88%E3%81%86%E3%81%AACSV%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AF%E5%B0%82%E7%94%A8%E3%81%AE%E3%83%84%E3%83%BC%E3%83%AB%E3%81%A7%E5%87%A6%E7%90%86%E3%81%99%E3%82%8C%E3%81%B0%E8%89%AF%E3%81%84%E3%80%8D%E3%81%A8%E3%81%84%E3%81%86%E6%80%9D%E6%83%B3%E3%81%A7%E9%80%B2%E3%82%81%E3%82%89%E3%82%8C%E3%81%BE%E3%81%97%E3%81%9F%E3%80%82)
> gawkの開発は「完全を目指すのではなく9割をサクサクこなし、フィールドに改行を含むようなCSVファイルは専用のツールで処理すれば良い」という思想で進められました。

上記制限を許容した上で利用する分には良いけれど、、と思っていたところ、[他にも色々手段がある](https://qiita.com/e99h2121/items/3d70cebae1131cebe276)

紹介されている中で最も定番ぽい印象を受けた [qコマンド](https://harelba.github.io/q/) について、調べたことを簡単にメモしておく

# 前提
```bash
$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.15.7
BuildVersion:	19H15

$ q -v
q version 3.1.6
Python: 3.9.7 (default, Oct 18 2021, 00:59:13) // [Clang 13.0.0 ]
Copyright (C) 2012-2021 Harel Ben-Attia (harelba@gmail.com, @harelba on twitter)
http://harelba.github.io/q/
```

まずは[インストール](https://harelba.github.io/q/#installation)しましょう

# 使い方
基本的には、以下のフォーマットで使っている

```bash
$ nkf -w file.csv | q -H -d, -O -T -q ./test.sql | less

# 補足
# 文字コード変換する必要がなければ、`nkf` する必要なし
# ヘッダーがない場合は、`-H` をなくして SQL のカラムは c1, c2, ... を使う
# SQL を直接指定する場合は、`-q ./test.sql` をなくす（例. "select * from -"）
```

## 入力
- 基本的には、`qコマンド` に入力する前に別ツールで文字コード変換している
    - 文字コード変換用のオプションがある
    - けれど、手元で試した時に上手く読み込めなかった（詳細は後述）


## オプション
- ヘッダー読み込み
    - `-H`
- 区切り文字指定
    - カンマ: `-d,`
    - タブ: `-t`
- ヘッダー出力
    - `-O`
        - ヘッダー変換したい場合は、上記を指定した上で AS 句を使う
- タブ区切り出力
    - `-T`
- SQLファイル実行
    - `-q [ファイルパス]`
        - ファイルに記載するSQLはクォートで囲む必要なし
- 文字コード指定
    - `-e data-encoding`
    - `-Q query-encoding`
    - `-E output-encoding`

## SQL
- [SQLite3](https://www.sqlite.org/lang.html) に[準拠している](https://harelba.github.io/q/#installation:~:text=All%20sqlite3%20SQL%20constructs%20are%20supported%2C%20including%20joins%20across%20files%20(use%20an%20alias%20for%20each%20table).%20Take%20a%20look%20at%20the%20limitations%20section%20below%20for%20some%20rarely%2Dused%20use%20cases%20which%20are%20not%20fully%20supported.)とのこと
    - [いくつか制限がある](https://harelba.github.io/q/#limitations)
        - サブクエリをfromに置けないとか（whereには書ける）
    - 句 `group by`, `having`, `join`, ...
    - 関数 `count`, `sum`, `substr`, ...
- from 指定
    - 標準入力: `-`
    - ファイル: `[ファイルパス]`
- カラム名
    - ヘッダー行あれば（`-H` 指定） 、ヘッダー名
    - なければ、c1, c2, ...

# 補足
## CSV のパース
いくつかバリエーションを試したが、特に問題なし

```bash
# フィールド内にカンマを含むケース
$ echo 'aaa,"bbb,ccc",ddd' | q -d, "select c2 from -"
=>
"bbb,ccc"

# フィールド内に改行を含むケース
$ echo -e "aaa,\"bbb\nccc\",ddd" | q -d, "select c2 from -"
=>
"bbb
ccc"

# フィールド内にダブルクオートそのものを含むケース
$ echo 'a,"b""c",d' | q -d, "select c2 from -"
=>
"b""c"

# フィールド内にエスケープされていない二重引用符を含むケース
# ※ 不正CSV（RFC 4180 に違反）
$ echo 'a,b"c,d' | q -d, "select c2 from -"
=>
"b""c"
```

## 文字コードの変換
Shift_JIS の CSV を読み込もうとしたらエラー
※ 日本語を含む 250MB 程のファイル

```bash
$ nkf -guess file.csv
Shift_JIS

$ cat file.csv | q -H -d, -e Shift_JIS -Q UTF-8 -E UTF-8 "select c1, count(1) from -" | less
=>
Could not parse the input. Please make sure to set the proper -w input-wrapping parameter for your input, and that you use the proper input encoding (-e). Error: 'utf-8' codec can't decode byte 0xca in position 293: invalid continuation byte
```

他のツールだと問題なく読み込める

```ruby
# 例. Ruby
CSV.read('file.csv', encoding: 'Shift_JIS:UTF-8')
```

日本語を含むファイルの文字コード変換は、他ツールを使った方が無難？

```bash
$ nkf -w file.csv | q -H -d, "select カラム1 from -" | less
```

# 終わりに
簡単に使い方を調べて、qコマンドの便利さを理解できたので、実際に使っていこうと思う（データ量が多い場合には、Athena を使えば事足りそう）

類似ツールは Go が多いようで早そうだけど、現状特に課題感はなくて当分は良さそう

後、今回のような些細な記事一つ取っても、改めて視野を広げて取り組まないといけないなと