Partout où je vois quelqu'un qui a besoin d'obtenir une liste triée et unique, il redirige toujours vers sort | uniq
. Je n'ai jamais vu d'exemples où quelqu'un utilise sort -u
au lieu. Pourquoi pas? Quelle est la différence, et pourquoi est-il préférable d'utiliser uniq que l'indicateur unique pour trier?
sort | uniq
existait avant sort -u
, et est compatible avec une plus large gamme de systèmes, bien que presque tous les systèmes modernes prennent en charge -u
- c'est POSIX. C'est surtout un retour aux jours où sort -u
n'existait pas (et les gens n'ont pas tendance à changer leurs méthodes si la façon dont ils savent continuer de fonctionner, regardez simplement ifconfig
contre ip
adoption).
Les deux ont probablement été fusionnés car la suppression des doublons dans un fichier nécessite un tri (au moins, dans le cas standard), et est un cas d'utilisation très courant du tri. Il est également plus rapide en interne car il peut effectuer les deux opérations en même temps (et en raison du fait qu'il ne nécessite pas IPC entre uniq
et sort
). Surtout si le fichier est volumineux, sort -u
utilisera probablement moins de fichiers intermédiaires pour trier les données.
Sur mon système, j'obtiens constamment des résultats comme celui-ci:
$ dd if=/dev/urandom of=/dev/shm/file bs=1M count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 8.95208 s, 11.7 MB/s
$ time sort -u /dev/shm/file >/dev/null
real 0m0.500s
user 0m0.767s
sys 0m0.167s
$ time sort /dev/shm/file | uniq >/dev/null
real 0m0.772s
user 0m1.137s
sys 0m0.273s
Il ne masque pas non plus le code de retour de sort
, ce qui peut être important (dans les shells modernes, il existe des moyens pour l'obtenir, par exemple, bash
's $PIPESTATUS
array, mais ce n'était pas toujours vrai).
Une différence est que uniq
a un certain nombre d'options supplémentaires utiles, telles que sauter des champs pour la comparaison et compter le nombre de répétitions d'une valeur. sort
's -u
flag n'implémente que la fonctionnalité de la commande uniq
sans fioritures.
Avec la compatibilité POSIX sort
s et uniq
s (GNU uniq
n'est actuellement pas conforme à cet égard), il y a une différence dans cette sort
utilise l'algorithme de classement des paramètres régionaux pour comparer les chaînes (utilise généralement strcoll()
pour comparer les chaînes) tandis que uniq
vérifie l'identité de la valeur en octets (utilise généralement strcmp()
) ¹ .
Cela compte pour au moins deux raisons.
Dans certains paramètres régionaux, en particulier sur les systèmes GNU, il existe différents caractères qui les trient de la même manière. Par exemple, dans les paramètres régionaux en_US.UTF-8 sur un GNU système, tous les ①②③④⑤⑥⑦⑧⑨⑩ ... caractères² et bien d'autres trient de la même manière car leur ordre de tri n'est pas défini. Les chiffres arabes 0123456789 sont triés de la même manière que leurs homologues Indic arabe oriental (٠١٢٣٤٥٦٧٨٩).
Pour sort -u
, ① trie la même chose que ② et 0123 la même chose que ٠١٢٣ afin que sort -u
N'en conserve qu'une seule, tandis que pour uniq
(et non GNU uniq
qui utilise strcoll()
(sauf avec -i
)), ① est différent de ② et 0123 différent de ٠١٢٣, donc uniq
serait considérez tous les 4 uniques.
strcoll
ne peut comparer que des chaînes de caractères valides (le comportement n'est pas défini selon POSIX lorsque l'entrée contient des séquences d'octets qui ne forment pas de caractères valides) tandis que strcmp()
ne se soucie pas des caractères car il ne fait que la comparaison octet à octet. C'est donc une autre raison pour laquelle sort -u
Peut ne pas vous donner toutes les lignes uniques si certaines d'entre elles ne forment pas de texte valide. sort|uniq
, Bien que non spécifié pour la saisie non textuelle, dans la pratique, il est plus susceptible de vous donner des lignes uniques pour cette raison.À côté de ces subtilités, une chose qui n'a pas été notée jusqu'à présent est que uniq
compare lexicalement toute la ligne, tandis que le -u
De sort
compare en fonction de la spécification de tri donnée sur la commande ligne.
$ printf '%s\n' 'a b' 'a c' | sort -uk 1,1
a b
$ printf '%s\n' 'a b' 'a c' | sort -k 1,1 | uniq
a b
a c
$ printf '%s\n' 0 -0 +0 00 '' | sort -n | uniq
0
-0
+0
00
$ printf '%s\n' 0 -0 +0 00 '' | sort -nu
0
¹ Les versions antérieures de la spécification POSIX causaient cependant de la confusion en répertoriant la variable LC_COLLATE
Comme une variable affectant uniq
, qui a été supprimée dans l'édition 2018 et le comportement clarifié à la suite de la discussion mentionnée ci-dessus. Voir le bug du groupe Austin correspondant
² Édition 2019 . Ceux-ci ont depuis été corrigés, mais plus de 95% des points de code Unicode ont toujours un ordre indéfini à partir de la version 2.30 de la GNU libc . Vous pouvez tester avec ?? ?????????????????? par exemple dans les versions plus récentes
Je préfère utiliser sort | uniq
parce que lorsque j'essaie d'utiliser le -u
(éliminer les doublons) pour supprimer les doublons impliquant des chaînes de casse mixtes, il n'est pas si facile de comprendre le résultat.
Remarque: avant de pouvoir exécuter les exemples ci-dessous, vous devez simuler la séquence de classement C standard en procédant comme suit:
LC_ALL=C
export LC_ALL
Par exemple, si je veux trier un fichier et supprimer les doublons, tout en gardant les différents cas de chaînes distincts.
$ cat short #file to sort
Pear
Pear
Apple
pear
Apple
$ sort short #normal sort (in normal C collating sequence)
Apple #the lower case words are at the end
Pear
Pear
Apple
pear
$ sort -f short #correctly sorts ignoring the C collating order
Apple #but duplicates are still there
Apple
Pear
Pear
pear
$ sort -fu short #By adding the -u option to remove duplicates it is
Apple #difficult to ascertain the logic that sort uses to remove
Pear #duplicates(i.e., why did it remove pear instead of Pear?)
Cette confusion est résolue en n'utilisant pas le -u
option pour supprimer les doublons. L'utilisation de uniq
est plus prévisible. L'exemple ci-dessous trie et ignore d'abord la casse, puis la transmet à uniq
pour supprimer les doublons.
$ sort -f short | uniq
Apple
apple
Pear
pear
Une autre différence que j'ai découverte aujourd'hui est le tri basé sur un délimiteur où sort -u
Applique l'indicateur unique uniquement sur la colonne avec laquelle vous triez.
$ cat input.csv
3,World,1
1,Hello,1
2,Hello,1
$ cat input.csv | sort -t',' -k2 -u
1,Hello,1
3,World,1
$ cat input.csv | sort -t',' -k2 | uniq
1,Hello,1
2,Hello,1
3,World,1