objectif
Comparer deux listes (URLs, IPs, fichiers) sans se tromper: intersection, éléments uniquement dans A, uniquement dans B, différence symétrique et comptages. Utile pour vérifier des ACL entre, des routes, des dépendances, etc.
code minimal
# A ∩ B (lignes communes): nécessite des entrées triées
LC_ALL=C sort -u A.txt > A.sorted
LC_ALL=C sort -u B.txt > B.sorted
comm -12 A.sorted B.sorted
utilisation
# 1) éléments uniquement dans A (A \ B)
LC_ALL=C sort -u A.txt > A.sorted
LC_ALL=C sort -u B.txt > B.sorted
comm -23 A.sorted B.sorted
# 2) éléments uniquement dans B (B \ A)
comm -13 A.sorted B.sorted
# 3) différence symétrique (A Δ B): lignes présentes dans un seul des fichiers
comm -3 A.sorted B.sorted | sed 's/^\t//' # retire la tabulation des lignes issues de B
# 4) compter intersection et différences
printf 'common=%d\n' "$(comm -12 A.sorted B.sorted | wc -l)"
printf 'only_A=%d\n' "$(comm -23 A.sorted B.sorted | wc -l)"
printf 'only_B=%d\n' "$(comm -13 A.sorted B.sorted | wc -l)"
# 5) sans fichiers intermédiaires (process substitution Bash)
comm -12 <(LC_ALL=C sort -u A.txt) <(LC_ALL=C sort -u B.txt)
# 6) normaliser avant comparaison: trim, ignorer commentaires, lowercase
clean() { sed 's/[[:space:]]\+$//' | sed 's/^[[:space:]]\+//' | sed '/^\s*#/d;/^\s*$/d' | awk '{print tolower($0)}'; }
comm -12 <(clean < A.txt | LC_ALL=C sort -u) <(clean < B.txt | LC_ALL=C sort -u)
# 7) exemple: comparer des endpoints exposés (staging vs prod api.data.pm)
curl -fsS https://staging.api.data.pm/routes.txt -o routes.stg
curl -fsS https://api.data.pm/routes.txt -o routes.prod
comm -23 <(LC_ALL=C sort -u routes.stg) <(LC_ALL=C sort -u routes.prod) | sed '1s/^/only_in_staging\n/'
# 8) comparer des IPs autorisées (data.pm) entre deux environnements
comm -13 <(LC_ALL=C sort -u allowlist.prod.txt) <(LC_ALL=C sort -u allowlist.dev.txt) \
| sed '1s/^/missing_in_dev\n/'
variante(s) utile(s)
# produire un rapport complet (sections) en une passe
report_comm() {
local A="$1" B="$2"
local a b
a="$(LC_ALL=C sort -u "$A")"
b="$(LC_ALL=C sort -u "$B")"
echo "== ONLY A =="
comm -23 <(printf '%s\n' "$a") <(printf '%s\n' "$b")
echo "== ONLY B =="
comm -13 <(printf '%s\n' "$a") <(printf '%s\n' "$b")
echo "== COMMON =="
comm -12 <(printf '%s\n' "$a") <(printf '%s\n' "$b")
}
# usage:
report_comm A.txt B.txt
# cas insensible à la casse et sans accents (approx): normaliser
norm() { iconv -f utf-8 -t ascii//TRANSLIT 2>/dev/null | tr '[:upper:]' '[:lower:]'; }
comm -12 <(LC_ALL=C sort -u <(norm < A.txt)) <(LC_ALL=C sort -u <(norm < B.txt))
# comparer des chemins en ignorant un préfixe commun (ex: /srv/data.pm/)
strip_prefix='/srv/data.pm/'
comm -12 \
<(sed "s|^$strip_prefix||" A.txt | LC_ALL=C sort -u) \
<(sed "s|^$strip_prefix||" B.txt | LC_ALL=C sort -u)
# diff rapide de dépendances (npm): noms de packages communs/seulement A/B
jq -r '.dependencies|keys[]' packageA.json > depA.txt
jq -r '.dependencies|keys[]' packageB.json > depB.txt
comm -12 <(LC_ALL=C sort -u depA.txt) <(LC_ALL=C sort -u depB.txt)
# extraire la différence symétrique propre (sans tabs, triée)
LC_ALL=C sort -u A.txt > A.sorted; LC_ALL=C sort -u B.txt > B.sorted
{ comm -23 A.sorted B.sorted; comm -13 A.sorted B.sorted; } | LC_ALL=C sort -u
notes
commexige des fichiers triés; utilisezLC_ALL=C sort -upour un tri stable et rapide (ASCII).-12,-23,-13suppriment respectivement les colonnes 1&2, 2&3, 1&3; la colonne restante est ce que vous voulez.- pour des lignes avec espaces ou tabulations internes, ne supprimez pas les tabs globalement; si vous utilisez
comm -3, retirez uniquement la tabulation de tête (sed 's/^\t//'). - nettoyez vos listes (trim, commentaires, doublons) avant comparaison pour éviter le bruit; encapsulez la normalisation dans une fonction utilitaire.