web-dev-qa-db-fra.com

Différence entre deux listes avec Bash

Ok, j'ai deux listes liées sur ma boîte Linux dans des fichiers texte:

 /tmp/oldList
 /tmp/newList

Je dois comparer ces listes pour voir quelles lignes ont été ajoutées et quelles lignes ont été supprimées. Je dois ensuite passer en boucle sur ces lignes et y effectuer des actions selon qu’elles ont été ajoutées ou supprimées.

Comment puis-je faire cela à bash?

23
exvance

Utilisez la commande comm(1) pour comparer les deux fichiers. Ils doivent tous les deux être triés, ce que vous pouvez faire au préalable s'ils sont volumineux, ou vous pouvez le faire en ligne avec bash process substitution .

comm peut combiner les indicateurs -1, -2 et -3 en indiquant le fichier à supprimer (unique pour le fichier 1, unique pour le fichier 2 ou commune aux deux).

Pour obtenir les lignes uniquement dans l'ancien fichier:

comm -23 <(sort /tmp/oldList) <(sort /tmp/newList)

Pour obtenir les lignes uniquement dans le nouveau fichier:

comm -13 <(sort /tmp/oldList) <(sort /tmp/newList)

Vous pouvez insérer cela dans une boucle while read pour traiter chaque ligne:

while read old ; do
    ...do stuff with $old
done < <(comm -23 <(sort /tmp/oldList) <(sort /tmp/newList))

et de même pour les nouvelles lignes.

58
camh

La commande diff fera la comparaison pour vous.

par exemple., 

$ diff /tmp/oldList /tmp/newList

Voir le lien de la page de manuel ci-dessus pour plus d'informations. Cela devrait prendre en charge votre première partie de votre problème.

5
Levon

Pensez à utiliser Ruby si vos scripts ont besoin de lisibilité.

Pour obtenir les lignes uniquement dans l'ancien fichier:

Ruby -e "puts File.readlines('/tmp/oldList') - File.readlines('/tmp/newList')"

Pour obtenir les lignes uniquement dans le nouveau fichier:

Ruby -e "puts File.readlines('/tmp/newList') - File.readlines('/tmp/oldList')"

Vous pouvez alimenter cela dans une boucle de lecture while pour traiter chaque ligne:

while read old ; do
  ...do stuff with $old
done < Ruby -e "puts File.readlines('/tmp/oldList') - File.readlines('/tmp/newList')"
3
Nowaker

Ceci est vieux, mais pour être complet, nous devrions dire que si vous avez un très grand ensemble, la solution la plus rapide serait d’utiliser diff pour générer un script, puis l’alimenter comme ceci:

#!/bin/bash

line_added() {
   # code to be run for all lines added
   # $* is the line 
}

line_removed() {
   # code to be run for all lines removed
   # $* is the line 
}

line_same() {
   # code to be run for all lines at are the same
   # $* is the line 
}

cat /tmp/oldList | sort >/tmp/oldList.sorted
cat /tmp/newList | sort >/tmp/newList.sorted

diff >/tmp/diff_script.sh \
    --new-line-format="line_added %L" \
    --old-line-format="line_removed %L" \
    --unchanged-line-format="line_same %L" \
    /tmp/oldList.sorted /tmp/newList.sorted

source /tmp/diff_script.sh

Les lignes modifiées apparaîtront comme supprimées et ajoutées. Si vous n'aimez pas cela, vous pouvez utiliser --changed-group-format. Consultez la page de manuel diff.

1
Costa Tsaousis

Avez-vous essayé diff

$ diff /tmp/oldList /tmp/newList

$ man diff
0
ssedano

J'utilise typiquement:

diff /tmp/oldList /tmp/newList | grep -v "Common subdirectories"

L'option grep -v inverse la correspondance:

-v, --invert-match Les lignes sélectionnées sont celles qui ne correspondent à aucun des pat -.__ spécifiés. sternes.

Donc, dans ce cas, il prend les résultats diff et omet ceux qui sont courants.

0
Nathan