seenest un tableau associatif auquel Awk passera chaque ligne du fichier. Si une ligne n'est pas dans le tableau, la valeur seen[$0]sera false. Le !est un opérateur logique NOT et inversera le faux en vrai. Awk imprimera les lignes où l'expression évalue à vrai. Les ++incréments de seensorte seen[$0] == 1qu'après la première fois une ligne soit trouvée, puis seen[$0] == 2, et ainsi de suite.
Awk évalue tout sauf 0et ""(chaîne vide) à true. Si une ligne en double est placé seenalors !seen[$0]évaluera false et la ligne ne sera pas écrit à la sortie.
Pour l'enregistrer dans un fichier, nous pouvons le faireawk '!seen[$0]++' merge_all.txt > output.txt
Akash Kandpal
5
Une mise en garde importante ici: si vous devez faire cela pour plusieurs fichiers, et que vous fixez plus de fichiers à la fin de la commande, ou utilisez un caractère générique… le tableau «vu» se remplira de lignes dupliquées de TOUS les fichiers. Si vous souhaitez plutôt traiter chaque fichier indépendamment, vous devrez faire quelque chose commefor f in *.txt; do gawk -i inplace '!seen[$0]++' "$f"; done
Nick K9
@ NickK9 que la déduplication cumulative sur plusieurs fichiers est géniale en soi. Bon conseil
# delete duplicate, consecutive lines from a file (emulates "uniq").# First line in a set of duplicate lines is kept, rest are deleted.
sed '$!N; /^\(.*\)\n\1$/!P; D'# delete duplicate, nonconsecutive lines from a file. Beware not to# overflow the buffer size of the hold space, or else use GNU sed.
sed -n 'G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P'
geekery ;-) +1, mais la consommation de ressources est inévitable.
Michael Krelin - hacker
3
«$! N; /^(.*)\n\1$/!P; D 'signifie "Si vous n'êtes pas à la dernière ligne, lisez une autre ligne. Maintenant, regardez ce que vous avez et si ce n'est PAS un truc suivi d'un retour à la ligne, puis de nouveau le même truc, imprimez le truc. Maintenant supprimez le truc (jusqu'à la nouvelle ligne). "
Bêta du
2
'G; s / \ n / && /; / ^ ([- ~] * \ n). * \ n \ 1 / d; s / \ n //; h; P 'signifie, à peu près, "Ajoutez tout l'espace d'attente à cette ligne, puis si vous voyez une ligne dupliquée jetez le tout, sinon copiez tout le désordre dans l'espace d'attente et imprimez la première partie (qui est la ligne que vous venez de read. "
Bêta du
La $!pièce est-elle nécessaire? Ne fait pas sed 'N; /^\(.*\)\n\1$/!P; D'la même chose? Je ne peux pas trouver d'exemple où les deux sont différents sur ma machine (fwiw j'ai essayé une ligne vide à la fin avec les deux versions et elles allaient toutes les deux bien).
eddi
1
Presque 7 ans plus tard et personne n'a répondu à @amichair ... <sniff> me rend triste. ;) Quoi qu'il en soit, [ -~]représente une plage de caractères ASCII de 0x20 (espace) à 0x7E (tilde). Ceux-ci sont considérés comme les caractères ASCII imprimables (la page liée a également 0x7F / delete mais cela ne semble pas correct). Cela rend la solution cassée pour quiconque n'utilise pas ASCII ou pour quiconque utilise, par exemple, des caractères de tabulation. Le plus portable [^\n]comprend beaucoup plus de caractères ... tous sauf un, en fait.
B Layer
14
Perl one-liner similaire à la solution awk de @ jonas:
perl -ne 'print if ! $x{$_}++' file
Cette variante supprime les espaces de fin avant de comparer:
perl -lne 's/\s*$//; print if ! $x{$_}++' file
Cette variante modifie le fichier sur place:
perl -i -ne 'print if ! $x{$_}++' file
Cette variante modifie le fichier sur place et effectue une sauvegarde file.bak
Le one-liner qu'Andre Miller a posté ci-dessus fonctionne à l'exception des versions récentes de sed lorsque le fichier d'entrée se termine par une ligne vide et sans caractères. Sur mon Mac, mon processeur tourne simplement.
Boucle infinie si la dernière ligne est vide et n'a pas de caractères :
sed '$!N; /^\(.*\)\n\1$/!P; D'
Ne se bloque pas, mais tu perds la dernière ligne
sed '$d;N; /^\(.*\)\n\1$/!P; D'
L'explication se trouve à la toute fin de la FAQ sed :
Le mainteneur de GNU sed a estimé que malgré les problèmes de portabilité que
cela causerait, changer la commande N pour imprimer (plutôt que
supprimer) l'espace des motifs était plus cohérent avec ses intuitions
sur la façon dont une commande pour "ajouter la ligne suivante" devrait se comporter.
Un autre fait en faveur du changement était que "{N; command;}"
supprimera la dernière ligne si le fichier a un nombre impair de lignes, mais
imprimera la dernière ligne si le fichier a un nombre pair de lignes.
Pour convertir des scripts qui utilisaient l'ancien comportement de N (suppression de
l'espace de motif en atteignant l'EOF) en scripts compatibles avec
toutes les versions de sed, changez un seul "N;" à "$ d; N;" .
print ONLY once of each duplicate consecutive lines at its LAST appearance and use D command to implement LOOP.
Explique:
$!N;: si la ligne courante n'est PAS la dernière ligne, utilisez la Ncommande pour lire la ligne suivante pattern space.
/^(.*)\n\1$/!P: si le contenu de current pattern spaceest deux duplicate stringséparés par \n, ce qui signifie que la ligne suivante est la sameavec la ligne courante, nous ne pouvons PAS l'imprimer selon notre idée de base; sinon, ce qui signifie que la ligne actuelle est la DERNIÈRE apparence de toutes ses lignes consécutives en double, nous pouvons maintenant utiliser la Pcommande pour imprimer les caractères dans l' pattern spaceutilitaire actuel \n( \négalement imprimé).
D: nous utilisons la Dcommande pour supprimer les caractères dans l' pattern spaceutilitaire actuel \n( \négalement supprimé), puis le contenu de pattern spaceest la ligne suivante.
et la Dcommande forcera sedà sauter à sa FIRSTcommande $!N, mais ne lira PAS la ligne suivante du fichier ou du flux d'entrée standard.
La deuxième solution est facile à comprendre (de moi-même):
print ONLY once of each duplicate consecutive lines at its FIRST appearance and use : command & t command to implement LOOP.
Explique:
lire une nouvelle ligne à partir du flux d'entrée ou du fichier et l'imprimer une fois.
utilisez la :loopcommande set un labelnamed loop.
utilisez Npour lire la ligne suivante dans le pattern space.
utilisez s/^(.*)\n\1$/\1/pour supprimer la ligne actuelle si la ligne suivante est la même que la ligne actuelle, nous utilisons la scommande pour faire l' deleteaction.
si la scommande est exécutée avec succès, alors utilisez la tloopforce de commande sedpour sauter à la labelnommée loop, qui fera la même boucle aux lignes suivantes car il n'y a pas de lignes consécutives en double de la ligne qui est latest printed; sinon, utilisez la Dcommande sur deletela ligne qui est la même que le latest-printed line, et forcez sedà sauter à la première commande, qui est la pcommande, le contenu de current pattern spaceest la nouvelle ligne suivante.
Qu'est-ce qu'un fichier texte de 20 Go? Trop lent.
Alexander Lubyagin
Comme toujours, le catest inutile. Quoi qu'il en soit, le fait uniqdéjà par lui-même et ne nécessite pas que l'entrée soit exactement un mot par ligne.
uniq
seul suffit.awk
, mais sera assez gourmand en ressources sur des fichiers plus volumineux.Réponses:
seen
est un tableau associatif auquel Awk passera chaque ligne du fichier. Si une ligne n'est pas dans le tableau, la valeurseen[$0]
sera false. Le!
est un opérateur logique NOT et inversera le faux en vrai. Awk imprimera les lignes où l'expression évalue à vrai. Les++
incréments deseen
sorteseen[$0] == 1
qu'après la première fois une ligne soit trouvée, puisseen[$0] == 2
, et ainsi de suite.Awk évalue tout sauf
0
et""
(chaîne vide) à true. Si une ligne en double est placéseen
alors!seen[$0]
évaluera false et la ligne ne sera pas écrit à la sortie.la source
awk '!seen[$0]++' merge_all.txt > output.txt
for f in *.txt; do gawk -i inplace '!seen[$0]++' "$f"; done
De http://sed.sourceforge.net/sed1line.txt : (Veuillez ne pas me demander comment cela fonctionne ;-))
la source
$!
pièce est-elle nécessaire? Ne fait passed 'N; /^\(.*\)\n\1$/!P; D'
la même chose? Je ne peux pas trouver d'exemple où les deux sont différents sur ma machine (fwiw j'ai essayé une ligne vide à la fin avec les deux versions et elles allaient toutes les deux bien).[ -~]
représente une plage de caractères ASCII de 0x20 (espace) à 0x7E (tilde). Ceux-ci sont considérés comme les caractères ASCII imprimables (la page liée a également 0x7F / delete mais cela ne semble pas correct). Cela rend la solution cassée pour quiconque n'utilise pas ASCII ou pour quiconque utilise, par exemple, des caractères de tabulation. Le plus portable[^\n]
comprend beaucoup plus de caractères ... tous sauf un, en fait.Perl one-liner similaire à la solution awk de @ jonas:
Cette variante supprime les espaces de fin avant de comparer:
Cette variante modifie le fichier sur place:
Cette variante modifie le fichier sur place et effectue une sauvegarde
file.bak
la source
Le one-liner qu'Andre Miller a posté ci-dessus fonctionne à l'exception des versions récentes de sed lorsque le fichier d'entrée se termine par une ligne vide et sans caractères. Sur mon Mac, mon processeur tourne simplement.
Boucle infinie si la dernière ligne est vide et n'a pas de caractères :
sed '$!N; /^\(.*\)\n\1$/!P; D'
Ne se bloque pas, mais tu perds la dernière ligne
sed '$d;N; /^\(.*\)\n\1$/!P; D'
L'explication se trouve à la toute fin de la FAQ sed :
la source
Une autre façon d'utiliser Vim (compatible Vi) :
Supprimer les lignes consécutives en double d'un fichier:
vim -esu NONE +'g/\v^(.*)\n\1$/d' +wq
Supprimer les lignes dupliquées, non consécutives et non vides d'un fichier:
vim -esu NONE +'g/\v^(.+)$\_.{-}^\1$/d' +wq
la source
La première solution vient également de http://sed.sourceforge.net/sed1line.txt
l'idée principale est:
Explique:
$!N;
: si la ligne courante n'est PAS la dernière ligne, utilisez laN
commande pour lire la ligne suivantepattern space
./^(.*)\n\1$/!P
: si le contenu de currentpattern space
est deuxduplicate string
séparés par\n
, ce qui signifie que la ligne suivante est lasame
avec la ligne courante, nous ne pouvons PAS l'imprimer selon notre idée de base; sinon, ce qui signifie que la ligne actuelle est la DERNIÈRE apparence de toutes ses lignes consécutives en double, nous pouvons maintenant utiliser laP
commande pour imprimer les caractères dans l'pattern space
utilitaire actuel\n
(\n
également imprimé).D
: nous utilisons laD
commande pour supprimer les caractères dans l'pattern space
utilitaire actuel\n
(\n
également supprimé), puis le contenu depattern space
est la ligne suivante.D
commande forcerased
à sauter à saFIRST
commande$!N
, mais ne lira PAS la ligne suivante du fichier ou du flux d'entrée standard.La deuxième solution est facile à comprendre (de moi-même):
l'idée principale est:
Explique:
:loop
commande set unlabel
namedloop
.N
pour lire la ligne suivante dans lepattern space
.s/^(.*)\n\1$/\1/
pour supprimer la ligne actuelle si la ligne suivante est la même que la ligne actuelle, nous utilisons las
commande pour faire l'delete
action.s
commande est exécutée avec succès, alors utilisez latloop
force de commandesed
pour sauter à lalabel
nomméeloop
, qui fera la même boucle aux lignes suivantes car il n'y a pas de lignes consécutives en double de la ligne qui estlatest printed
; sinon, utilisez laD
commande surdelete
la ligne qui est la même que lelatest-printed line
, et forcezsed
à sauter à la première commande, qui est lap
commande, le contenu de currentpattern space
est la nouvelle ligne suivante.la source
busybox echo -e "1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5" | busybox sed -nr "$!N;/^(.*)\n\1$/!P;D"
Ceci peut être réalisé en utilisant awk
sous la ligne affichera des valeurs uniques
Vous pouvez générer ces valeurs uniques dans un nouveau fichier
le nouveau fichier uniq_file_name ne contiendra que des valeurs uniques, pas de doublons
la source
Supprime les lignes dupliquées à l'aide de awk.
la source
cat
est inutile. Quoi qu'il en soit, le faituniq
déjà par lui-même et ne nécessite pas que l'entrée soit exactement un mot par ligne.