Je suis sûr que j'ai trouvé une fois une commande unix qui pourrait imprimer les lignes communes de deux fichiers ou plus, est-ce que quelqu'un connaît son nom? C'était beaucoup plus simple que diff
.
unix
shell
command-line
trop de php
la source
la source
comm
nécessitent des fichiers d'entrée triés. Si vous voulez juste du commun ligne par ligne, c'est génial. Mais si vous voulez ce que j'appellerais "anti-diff",comm
cela ne fait pas l'affaire.pr-123-xy-45
et fichier2 contientec11_orop_pr-123-xy-45.gz
. J'ai besoin d'un fichier3 contenantec11_orop_pr-123-xy-45.gz
Réponses:
La commande que vous recherchez est
comm
. par exemple:-Ici:
-1 : supprimer la colonne 1 (lignes uniques à 1.sorted.txt)
-2 : supprimer la colonne 2 (lignes uniques à 2.sorted.txt)
la source
grep
fait des choses étranges auxquelles vous ne vous attendez peut-être pas. Plus précisément, tout dans1.txt
sera interprété comme une expression régulière et non comme une chaîne simple. En outre, toute ligne vide correspondra à1.txt
toutes les lignes de2.txt
. Celagrep
ne fonctionnera donc que dans des situations très spécifiques. Vous voudriez au moins utiliserfgrep
(ougrep -f
) mais la ligne blanche va probablement faire des ravages sur ce processus.grep -F -x -f file1 file2
comm
commande dans 3 fichiers séparés? La réponse était beaucoup trop grande pour tenir confortablement ici.Pour appliquer facilement la commande comm à des fichiers non triés , utilisez la substitution de processus de Bash :
Ainsi les fichiers abc et def ont une ligne en commun, celle avec "132". Utilisation de la communication sur des fichiers non triés:
$ comm abc def 123 132 567 132 777 321 $ comm -12 abc def # No output! The common line is not found $
La dernière ligne n'a produit aucune sortie, la ligne commune n'a pas été découverte.
Maintenant, utilisez comm sur les fichiers triés, en triant les fichiers avec la substitution de processus:
Maintenant, nous avons la ligne 132!
la source
sort abc > abc.sorted
,sort dev > def.sorted
puiscomm -12 abc.sorted def.sorted
?Pour compléter le one-liner Perl, voici son
awk
équivalent:awk 'NR==FNR{arr[$0];next} $0 in arr' file1 file2
Cela lira toutes les lignes du
file1
tableauarr[]
, puis vérifiera chaque lignefile2
si elle existe déjà dans le tableau (c'est-à-direfile1
). Les lignes trouvées seront imprimées dans l'ordre dans lequel elles apparaissentfile2
. Notez que la comparaisonin arr
utilise la ligne entière defile2
comme index au tableau, elle ne rapportera donc que les correspondances exactes sur des lignes entières.la source
perl
ceux - là, parce que). Merci un million, MmePeut-être que tu veux dire
comm
?Le secret pour trouver ces informations sont les pages d'informations. Pour les programmes GNU, ils sont beaucoup plus détaillés que leurs pages de manuel. Essayez
info coreutils
et il vous listera tous les petits outils utiles.la source
Tandis que
vous donne les différences de deux fichiers (ce qui est en 2.txt et non en 1.txt), vous pouvez facilement faire un
pour collecter toutes les lignes courantes, ce qui devrait fournir une solution simple à votre problème. Si vous avez trié des fichiers, vous devriez
comm
quand même prendre . Cordialement!la source
grep
fait des choses étranges auxquelles vous ne vous attendez peut-être pas. Plus précisément, tout dans1.txt
sera interprété comme une expression régulière et non comme une chaîne simple. En outre, toute ligne vide correspondra à1.txt
toutes les lignes de2.txt
. Cela ne fonctionnera donc que dans des situations très spécifiques.grep
notations POSIX , qui sont prises en charge par lesgrep
variantes d'Unix les plus modernes. Ajoutez-F
(ou utilisezfgrep
) pour supprimer les expressions régulières. Ajouter-x
(pour exact) pour ne correspondre qu'à des lignes entières.comm
des fichiers triés?comm
peut travailler avec des fichiers arbitrairement volumineux tant qu'ils sont triés car il n'a besoin que de trois lignes en mémoire (je suppose que GNUcomm
saurait même ne garder qu'un préfixe si les lignes sont vraiment longues). Lagrep
solution doit conserver toutes les expressions de recherche en mémoire.Si les deux fichiers ne sont pas encore triés, vous pouvez utiliser:
et cela fonctionnera, en évitant le message d'erreur
comm: file 2 is not in sorted order
en faisantcomm -12 a.txt b.txt
.la source
<(command)
n'est pas portable pour le shell POSIX, bien qu'elle fonctionne dans Bash et quelques autres.perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' file1 file2
la source
comm
commande comme il recherche chaque ligne defile1
dansfile2
laquellecomm
ne comparera si la lignen
enfile1
est égale à la lignen
dansfile2
.comm
ne compare pas simplement la ligne N dans fichier1 avec la ligne N dans fichier2. Il peut parfaitement gérer une série de lignes insérées dans l'un ou l'autre fichier (ce qui équivaut à supprimer une série de lignes de l'autre fichier, bien sûr). Cela nécessite simplement que les entrées soient triées.comm
réponses si l'on veut garder l'ordre. Mieux vautawk
répondre si l'on ne veut pas de doublons.awk 'NR==FNR{a[$1]++;next} a[$1] ' file1 file2
la source
Sur une version limitée de Linux (comme un QNAP (nas) sur lequel je travaillais):
grep -f file1 file2
peut causer des problèmes comme le dit @ChristopherSchultz et l'utilisationgrep -F -f file1 file2
était vraiment lente (plus de 5 minutes - pas fini - plus de 2-3 secondes avec la méthode ci-dessous sur des fichiers de plus de 20 Mo)Alors voici ce que j'ai fait:
sort file1 > file1.sorted sort file2 > file2.sorted diff file1.sorted file2.sorted | grep "<" | sed 's/^< *//' > files.diff diff file1.sorted files.diff | grep "<" | sed 's/^< *//' > files.same.sorted
Si
files.same.sorted
doit avoir été dans le même ordre que les originaux, alors ajoutez cette ligne pour le même ordre que file1:awk 'FNR==NR {a[$0]=$0; next}; $0 in a {print a[$0]}' files.same.sorted file1 > files.same
ou, pour le même ordre que file2:
awk 'FNR==NR {a[$0]=$0; next}; $0 in a {print a[$0]}' files.same.sorted file2 > files.same
la source
À titre de référence, si quelqu'un cherche toujours à faire cela pour plusieurs fichiers, consultez la réponse liée à la recherche de lignes correspondantes dans de nombreux fichiers.
En combinant ces deux réponses ( ans1 et ans2 ), je pense que vous pouvez obtenir le résultat dont vous avez besoin sans trier les fichiers:
#!/bin/bash ans="matching_lines" for file1 in * do for file2 in * do if [ "$file1" != "$ans" ] && [ "$file2" != "$ans" ] && [ "$file1" != "$file2" ] ; then echo "Comparing: $file1 $file2 ..." >> $ans perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' $file1 $file2 >> $ans fi done done
Enregistrez-le simplement, donnez-lui les droits d'exécution (
chmod +x compareFiles.sh
) et exécutez-le. Il prendra tous les fichiers présents dans le répertoire de travail courant et fera une comparaison tout contre tout en laissant dans le fichier "matching_lines" le résultat.Choses à améliorer:
la source
rm file3.txt cat file1.out | while read line1 do cat file2.out | while read line2 do if [[ $line1 == $line2 ]]; then echo $line1 >>file3.out fi done done
Cela devrait le faire.
la source
rm -f file3.txt
si vous allez supprimer le fichier; cela ne rapportera aucune erreur si le fichier n'existe pas. OTOH, ce ne serait pas nécessaire si votre script faisait simplement écho à la sortie standard, laissant l'utilisateur du script choisir où la sortie devrait aller. En fin de compte, vous voudrez probablement utiliser$1
et$2
(arguments de ligne de commande) au lieu de noms de fichiers fixes (file1.out
etfile2.out
). Cela laisse l'algorithme: ça va être lent. Il va lirefile2.out
une fois pour chaque lignefile1.out
. Ce sera lent si les fichiers sont volumineux (disons plusieurs kilo-octets).grep -F
qui lit un fichier en mémoire puis effectue un seul passage sur l'autre évite de boucler à plusieurs reprises sur les deux fichiers d'entrée.