シェルで行を要素とし、集合で扱うための操作集
Lastmod: 2025-08-12

「ファイルや標準出力の差分を取りたい」「和を取りたい」というケースがたまに発生するので、そのとき組み合わせることができるようにスニペットを置いておく。

早見表

操作コマンド補足など
(cmd1; cmd2) | sort -u$cmd1 \cup cmd2$
cmd1 | grep -vf <(cmd2)$cmd1 \setminus cmd2$
(cmd1 | sort -u; cmd2 | sort -u) | sort | uniq -d$cmd1 \cap cmd2$
排他的論理和(cmd1; cmd2) | sort | uniq -u$cmd1 \veebar cmd2$

サンプルファイル

奇数の集合とフィボナッチ数の集合を用意した。

$ cat 1.txt
1
3
5
7
9
11
13
15
$ cat 2.txt
1
1
2
3
5
8
13
21

$ (cat 1.txt; cat 2.txt) | sort -u
1
11
13
15
2
21
3
5
7
8
9

uniq でいけるかと思いきや、uniq は隣接行しか評価しないため、事前に sort しておかないといけない。それなら sort-u オプションを付けたほうが手数が少ない。

$ (cat 1.txt; cat 2.txt) | uniq
1
3
5
7
9
11
13
15
1
2
3
5
8
13
21

もちろん、 sort | uniq も機能する。

$ (cat 1.txt; cat 2.txt) | sort | uniq
1
11
13
15
2
21
3
5
7
8
9

$ cat 1.txt | grep -vf <(cat 2.txt)
7
9

もし grep に引く方(cat 2.txt)を正規表現として解釈してもらいたいときは -e を付与する

$ (cat 1.txt | sort -u; cat 2.txt | sort -u) | sort | uniq -d
1
13
3
5

後ろから見るとわかりやすい。

  • uniq -d は隣接する重複行だけ出力する。
  • 隣接する、という条件があるので事前に sort を実行している。
  • 二つの重複していない集合同士の和において、重複した要素というのはすなわち積集合の要素である。よって、それぞれのコマンドについてユニークをとってから和を取っている。

もし 1.txt と 2.txt に重複がないのであれば、ユニークにするための操作を省いて

(cat 1.txt; cat 2.txt) | sort | uniq -d

としてよい。

排他的論理和

使う出番はあるのだろうか…

(cat 1.txt; cat 2.txt) | sort | uniq -u
11
15
2
21
7
8
9

おさらい: 排他的論理和は論理学において、どちらかが真のときに真になり、両方が真、または、偽のときに偽となる。

uniq -u はユニークである行だけ出力する。

番外編: 基本操作たち

重複削除

$ cat 2.txt | sort -u
1
13
2
21
3
5
8

重複だけ抽出

$ cat 2.txt | uniq -d
1

重複していない要素だけ抽出

$ cat 2.txt | uniq -u
2
3
5
8
13
21

さいごに

世にある記事は割と間違っていた。自分はこれを参考にしていこうと思う。