Skip to content
shellmap

commThree-column set comparison of two sorted files across all 5 shells

Equivalents in every shell

Bashunix
comm file1.txt file2.txt

Requires BOTH inputs to be sorted (the algorithm is single-pass merge). Output is three tab-separated columns: col1 = only in file1, col2 = only in file2, col3 = in both. Suppress columns with `-1`, `-2`, `-3` (e.g. `comm -12 a b` = lines in both).

Zshunix
comm file1.txt file2.txt

Same external `/usr/bin/comm` binary as bash. Zsh process substitution helps: `comm <(sort a.txt) <(sort b.txt)` saves the explicit pre-sort step.

Fishunix
comm file1.txt file2.txt

Same binary. Fish doesn't have process substitution syntactically — use `sort a.txt | psub` instead: `comm (sort a.txt | psub) (sort b.txt | psub)`.

PowerShellwindows
Compare-Object (Get-Content file1.txt) (Get-Content file2.txt)

`Compare-Object` is the conceptual analog but DIFFERENT: it does object diff with a `SideIndicator` column (`<=` only-in-reference, `=>` only-in-difference) — does NOT require sorted input. For three-column comm-style output, use `-IncludeEqual` and group on `SideIndicator`. The pure pwsh-native equivalent for "lines in both" is `(Get-Content a) | Where-Object { $_ -in (Get-Content b) }` — O(n²) but readable.

cmd.exewindows
fc /b file1.txt file2.txt

No native cmd `comm`. `fc /b` does BINARY compare (byte-level diff with offsets), `fc /L` does line compare but with diff-style output (not column suppression). For true comm-style set comparison, shell out to pwsh: `powershell -Command "Compare-Object (gc a) (gc b)"`.

Worked examples

List lines present in BOTH files (intersection)

Bash
comm -12 <(sort a.txt) <(sort b.txt)
Zsh
comm -12 <(sort a.txt) <(sort b.txt)
Fish
comm -12 (sort a.txt | psub) (sort b.txt | psub)
PowerShell
Compare-Object (Get-Content a.txt) (Get-Content b.txt) -IncludeEqual | Where-Object SideIndicator -eq "==" | Select-Object -Expand InputObject

List lines only in file1 (difference a − b)

Bash
comm -23 <(sort a.txt) <(sort b.txt)
Fish
comm -23 (sort a.txt | psub) (sort b.txt | psub)
PowerShell
Compare-Object (gc a.txt) (gc b.txt) | Where-Object SideIndicator -eq "<=" | Select-Object -Expand InputObject
cmd.exe
powershell -Command "Compare-Object (gc a.txt) (gc b.txt) | ? SideIndicator -eq \"<=\" | %% { $_.InputObject }"

Symmetric difference (lines in either but not both)

Bash
comm -3 <(sort a.txt) <(sort b.txt)
Fish
comm -3 (sort a.txt | psub) (sort b.txt | psub)
PowerShell
Compare-Object (gc a.txt) (gc b.txt) | Select-Object -Expand InputObject

Gotchas

  • BOTH inputs MUST be sorted with the same collation. `comm` walks them in lockstep and breaks the moment ordering diverges — GNU comm 8.30+ at least PRINTS `comm: file 1 is not in sorted order` on stderr, older versions silently produce wrong output. Always pre-sort with `LC_ALL=C sort` for byte-stable ordering.
  • Sort COLLATION matters: `sort` with default locale (`en_US.UTF-8`) treats `A` and `a` as adjacent, mixed-case files sort case-insensitively. Pre-sort BOTH files with the SAME collation: `LC_ALL=C sort` for byte-order (recommended for code/IDs), or pass `--check-order` to `comm` to catch divergence early.
  • Column suppression flags are NEGATIVES — `-1` HIDES col 1 (only-in-file1). To show ONLY lines in both, you hide cols 1 and 2: `comm -12`. Easy to flip backwards. Mnemonic: "`-12` shows column 3 (both)", "`-3` shows columns 1+2 (only-in-X)".
  • `Compare-Object` is NOT comm-equivalent in two key ways: (1) it does NOT require sorted input (uses internal hashing), so O(n+m) hash space vs comm's O(1) sequential — large inputs blow up memory in pwsh; (2) output is OBJECTS with `InputObject` + `SideIndicator`, not text columns — downstream consumers must unwrap `.InputObject`.
  • `comm` output is TAB-separated (col 1, then tab, col 2, then tab, col 3). When the inputs contain tabs themselves, the output becomes ambiguous — there's no quoting. Workaround: use `--output-delimiter=,` (GNU coreutils 8.6+) to switch to commas, or pre-process inputs to strip tabs.

WSL & PowerShell Core notes

pwsh`Compare-Object` is the cross-platform pwsh native and works identically on every pwsh OS — but its semantic gap from `comm` (object-pipeline, no sort requirement, different output shape) means cross-shell scripts that depend on tab-delimited 3-column text output need to format `Compare-Object` output explicitly: `... | ForEach-Object { "$($_.InputObject)`t$($_.SideIndicator)" }`. For pwsh-and-bash interoperable scripts, the `comm` binary plus pre-sort is more portable than `Compare-Object` plus formatting.
WSLWSL `comm` is GNU coreutils — identical to native Linux. Cross-OS file comparison (`comm` between a Windows file and a Linux file) needs CRLF normalisation first — Windows-saved files have `\r\n` line endings, Linux has `\n`, so EVERY line will compare as different. Pre-process: `comm <(tr -d "\r" < /mnt/c/win-file.txt | sort) <(sort linux-file.txt)`.

Related commands