Vous pouvez y parvenir en contrôlant le formatage des anciennes / nouvelles / lignes inchangées dans la diff
sortie GNU :
diff --new-line-format="" --unchanged-line-format="" file1 file2
Les fichiers d'entrée doivent être triés pour que cela fonctionne. Avec bash
(et zsh
), vous pouvez trier sur place avec la substitution de processus <( )
:
diff --new-line-format="" --unchanged-line-format="" <(sort file1) <(sort file2)
Dans les lignes ci-dessus , les lignes nouvelles et inchangées sont supprimées, donc seules les lignes modifiées (c'est-à-dire les lignes supprimées dans votre cas) sont sorties. Vous pouvez également utiliser quelques diff
options d' autres solutions n'offrent pas, comme -i
pour ignorer la casse, ou diverses options (espaces blancs -E
, -b
, -v
etc) pour la correspondance moins stricte.
Explication
Les options --new-line-format
, --old-line-format
et --unchanged-line-format
vous permettent de contrôler la façon de diff
formater les différences, comme pour les printf
spécificateurs de format. Ces options mettent en forme respectivement les lignes nouvelles (ajoutées), anciennes (supprimées) et inchangées . Mettre un à vide "" empêche la sortie de ce type de ligne.
Si vous connaissez le format diff unifié , vous pouvez le recréer en partie avec:
diff --old-line-format="-%L" --unchanged-line-format=" %L" \
--new-line-format="+%L" file1 file2
Le %L
spécificateur est la ligne en question, et nous préfixons chacun avec "+" "-" ou "", comme diff -u
(notez qu'il ne produit que des différences, il manque les lignes ---
+++
et @@
en haut de chaque changement groupé). Vous pouvez également l'utiliser pour faire d'autres choses utiles comme numéroter chaque ligne avec %dn
.
La diff
méthode (avec d'autres suggestions comm
et join
) ne produit que la sortie attendue avec une entrée triée , bien que vous puissiez utiliser <(sort ...)
pour trier sur place. Voici un awk
script simple (nawk) (inspiré des scripts liés dans la réponse de Konsolebox) qui accepte les fichiers d'entrée commandés arbitrairement et génère les lignes manquantes dans l'ordre dans lequel elles se produisent dans file1.
# output lines in file1 that are not in file2
BEGIN { FS="" } # preserve whitespace
(NR==FNR) { ll1[FNR]=$0; nl1=FNR; } # file1, index by lineno
(NR!=FNR) { ss2[$0]++; } # file2, index by string
END {
for (ll=1; ll<=nl1; ll++) if (!(ll1[ll] in ss2)) print ll1[ll]
}
Cela stocke le contenu entier de file1 ligne par ligne dans un tableau indexé de numéro de ligne ll1[]
, et le contenu entier de file2 ligne par ligne dans un tableau associatif indexé de contenu de ligne ss2[]
. Une fois les deux fichiers lus, parcourez ll1
et utilisez l' in
opérateur pour déterminer si la ligne dans file1 est présente dans file2. (Cela aura une sortie différente de la diff
méthode s'il y a des doublons.)
Dans le cas où les fichiers sont suffisamment volumineux pour que leur stockage entraîne un problème de mémoire, vous pouvez échanger la CPU contre de la mémoire en stockant uniquement file1 et en supprimant les correspondances en cours de lecture.
BEGIN { FS="" }
(NR==FNR) { # file1, index by lineno and string
ll1[FNR]=$0; ss1[$0]=FNR; nl1=FNR;
}
(NR!=FNR) { # file2
if ($0 in ss1) { delete ll1[ss1[$0]]; delete ss1[$0]; }
}
END {
for (ll=1; ll<=nl1; ll++) if (ll in ll1) print ll1[ll]
}
Ce qui précède stocke l'intégralité du contenu de file1 dans deux tableaux, l'un indexé par numéro de ligne ll1[]
, l'autre indexé par contenu de ligne ss1[]
. Lorsque le fichier2 est lu, chaque ligne correspondante est supprimée de ll1[]
et ss1[]
. À la fin, les lignes restantes du fichier1 sont sorties, préservant l'ordre d'origine.
Dans ce cas, avec le problème indiqué, vous pouvez également diviser et conquérir à l' aide de GNU split
(le filtrage est une extension GNU), des exécutions répétées avec des morceaux de fichier1 et la lecture complète de fichier2 à chaque fois:
split -l 20000 --filter='gawk -f linesnotin.awk - file2' < file1
Notez l'utilisation et le placement du -
sens stdin
sur la gawk
ligne de commande. Ceci est fourni par à split
partir de file1 en morceaux de 20000 lignes par appel.
Pour les utilisateurs sur les systèmes non-GNU, il est presque certainement un paquet coreutils GNU , vous pouvez obtenir, y compris sur OSX dans le cadre des d' Apple Xcode outils qui fournit GNU diff
, awk
, mais seulement un POSIX / BSD split
plutôt que d' une version GNU.
awk 'NR==FNR{a[$0];next}!($0 in a)' file2 file1 > out.txt