Every time I see things like this, I feel like the person must be unaware of awk.
# the original one-liner to get unique IP addresses
cut -d' ' -f 1 access.log | sort | uniq -c | sort -rn | head
# turns into this with GNU awk
gawk '{PROCINFO["sorted_in"] = "@val_num_desc"; a[$1]++} END {c=0; for (i in a) if (c++ < 10) print a[i], i}' access.log
It's also far, far faster on larger files (base-spec M1 Air):
$ wc -lc fake_log.txt
1000000 218433264 fake_log.txt
$ hyperfine "gawk '{PROCINFO[\"sorted_in\"] = \"@val_num_desc\"; a[\$1]++} END {c=0; for (i in a) if (c++ <10) print a[i], i}' fake_log.txt"
Benchmark 1: gawk '{PROCINFO["sorted_in"] = "@val_num_desc"; a[$1]++} END {c=0; for (i in a) if (c++ <10) print a[i], i}' fake_log.txt
Time (mean ± σ): 1.250 s ± 0.003 s [User: 1.185 s, System: 0.061 s]
Range (min … max): 1.246 s … 1.254 s 10 runs
$ hyperfine "cut -d' ' -f1 fake_log.txt | sort | uniq -c | sort -rn | head"
Benchmark 1: cut -d' ' -f1 fake_log.txt | sort | uniq -c | sort -rn | head
Time (mean ± σ): 4.844 s ± 0.020 s [User: 5.367 s, System: 0.087 s]
Range (min … max): 4.817 s … 4.873 s 10 runs
Interestingly, GNU cut is significantly faster than BSD cut on the M1:
$ hyperfine "gcut -d' ' -f1 fake_log.txt | sort | uniq -c | sort -rn | head"
Benchmark 1: gcut -d' ' -f1 fake_log.txt | sort | uniq -c | sort -rn | head
Time (mean ± σ): 3.622 s ± 0.004 s [User: 4.149 s, System: 0.078 s]
Range (min … max): 3.616 s … 3.629 s 10 runs