shell script: 2つのcsvファイルを縦に並べ，集合演算を用いて差分を取り出す．

# はじめに
　2つのファイルの差分をとりたいことがある[[1]]．代表的な方法に`diff`，`grep`，`comm`を用いた方法がある[[2]]．他に，集合演算で差集合を求めることで差分を取り出せる[[3]]．集合演算で行う場合，手順が増えるが，概念的に理解しやすいという利点もある．個人で扱う程度の小規模なデータであれば実用できると思われる．例えば，以前から収集していた保存データと，新たに集めたデータとの差分をとり，その差分をあらたに保存データに加える，といった利用方法が考えられる．収集するデータの保存形式としてテキストやcsv形式が考えられる．ここではデータ整理を意識してcsvファイルで行う．また，集合演算には`sort`と`uniq`を組み合わせる方法[[3]]と`sort`と`join`を組み合わせる方法とがある[[8]]．ここでは前者を用いる．

# 環境
MacOS: 10.15.3
zsh: 5.7.1

# 準備
## CSV形式について．
 csv形式はテキストエディタで編集できる，またExelやNumbersといった様々な表計算ソフトで扱える．すなわち，様々な環境で扱えるのがcsv形式の利点である[[4]]，一方，csvの書き方には様々な"方言"があり，よく定義されているといは言い難く，それが原因で不都合を生ずることがある[[5]][[6]]．あらゆる状況に対応できるようにするのはかなり大変である．そこで，ここでは個人利用を想定し，自分なりの書き方を決めておくことにして，なるべく簡素に書くことにする．うまくいかない場合は置換など用いて適宜対応することにする．例えば以下の様にする．

- 要素をあえてダブルクオートで挟まない．
- 区切りはカンマ`,`とする．
- 改行はあえて'\n'のみを扱う．
- など

## 要素について
- 1行を1つの要素として扱う．

## 練習用のファイルを2つ準備
- 練習用に2つのcsvファイルを準備する`file_1.csv`，`file_2.csv`．
- 1列目は収集したwebページのurl，2列目はtitleをいれる．
- file_1.csvはwebページ5件分，すなわち5行．file_2.csvは10件分，すなわち10行．
- 共通部分が2行分あるようにする．

```zsh:file_1.csvの内容をcatで表示する．
cat file_1.csv
url_1,title_1
url_2,title_2
url_3,title_3
url_4,title_4
url_5,title_5
```
```zsh:file_2.csvの内容をcatで表示する．
cat file_1.csv
url_1,title_4
url_2,title_5
url_3,title_6
url_4,title_7
url_5,title_8
url_5,title_9
url_5,title_10
url_5,title_11
url_5,title_12
url_5,title_13
```


## 2つのファイルを縦に並べる．
- ここが一番重要なところである．
- 過去の記事を参考にし，ファイルを縦に並べて集合演算を行う．
- 最終行末尾の改行の有無を気にせず2つのファイルを連結したいときは以下の`awk`を用いれば，盲目的に操作でき，簡潔[[7]]．

```zsh:awkを用いた盲目的連結方法
awk 1 'file_1.csv' 'file_2.csv'
```

# 集合演算概要
- 2つのファイルを縦に並べる．
- `sort`で順に並べる．
- `uniq`で重複して数えない様にする（和集合）．
- `uniq -d`では重複すると行が数えられる（積集合）．
- `uniq -u`では重複しないところだけ数えられる（排他的和集合）．
- 和集合とfile_1を縦に並べ，`sort`で並べ替え，重ならないところを数える（差集合）．


# 和集合（union, A OR B）
- 13行分が表示される．

```zsh
awk 1 'file_1.csv' 'file_2.csv' | sort | uniq
```
# 積集合（intersection, A AND B）
- 2行分が表示される．

```zsh
awk 1 'file_1.csv' 'file_2.csv' | sort | uniq -d
```

# 排他的論理和（exclusive union, A XOR B）
- 11行分が表示される．

```zsh

awk 1 'file_1.csv' 'file_2.csv' | sort | uniq -u
```

# 差集合（difference set, A - B）
- 和集合とfile_2.csvとを重ね，重ならないところを取り出す．
- 2つ目のawkは左から和集合を受け，右からfile_2.csvを受ける．
- パイプではなくセミコロン`;`にする．かっこ`()`で挟む[[3]]．

```zsh
(awk 1 file_1.csv file_2.csv | uniq \
; awk 1 file_2.csv) \
| sort \
| uniq -u
```

# 差集合（difference set, B - A）

```zsh
(awk 1 file_1.csv file_2.csv | uniq \
; cat file_1.csv) \
| sort \
| uniq -u
```

# 結果，考察
- リダイレクトを用いれば，差分を元のファイルに追加できる．

# まとめ
- 集合演算を用いて2つのcsvファイルの差分をとりだす方法を述べた．

# 参考
\[1]https://superuser.com/questions/234810/how-do-i-compare-two-files-with-a-shell-script
bash - How do I compare two files with a shell script? - Super User
\[2]https://qiita.com/mekagazira/items/1a1791a42e435cefd5f6
２つのファイルの共通行を抽出する方法 - Qiita
\[3]https://kunst1080.hatenablog.com/entry/2015/01/25/011158
uniqコマンドを使って、論理和・論理積・排他的論理和・差集合を得る方法 - くんすとの備忘録
\[4]https://shikaku.wakarutodekiru.com/blog/mame/2933.html
CSVファイルとは何？何のために使うの？
\[5]https://codezine.jp/article/detail/2364
CSVファイルフォーマットの解説：CodeZine（コードジン）
\[6]https://postd.cc/so-you-want-to-write-your-own-csv-code/
それでも独自のCSVを書くつもりですか？ | POSTD
\[7]https://qiita.com/BlackCat_617/items/f587af94fca62a23f0dc
ShellScript: 2つのテキストファイルを縦に連結する． - Qiita
\[8]https://qiita.com/highfrontier/items/610cd285f0c0de480ac9
コマンドラインで集合演算 - Qiita

[1]:https://superuser.com/questions/234810/how-do-i-compare-two-files-with-a-shell-script
[2]:https://qiita.com/mekagazira/items/1a1791a42e435cefd5f6
[3]:https://kunst1080.hatenablog.com/entry/2015/01/25/011158
[4]:https://shikaku.wakarutodekiru.com/blog/mame/2933.html
[5]:https://codezine.jp/article/detail/2364
[6]:https://postd.cc/so-you-want-to-write-your-own-csv-code/
[7]:https://qiita.com/BlackCat_617/items/f587af94fca62a23f0dc
[8]:https://qiita.com/highfrontier/items/610cd285f0c0de480ac9