J'ai un fichier CSV qui ressemble à ceci
AS2345, ASDF1232, Mr. Plain Example, 110 Binary ave., Atlantis, RI, 12345, (999) 123-5555,1.56 AS2345, ASDF1232, Mme Plain Example, 1121110 Ternary st. 110 avenue binaire .., Atlantis, RI, 12345, (999) 123-5555,1.56 AS2345, ASDF1232, M. Plain Example, 110 Binary ave., Liberty City, RI, 12345, (999) 123-5555,1.56 AS2345, ASDF1232, Mr. Plain Example, 110 Ternary ave., Some City, RI, 12345, (999) 123-5555,1.56
Je dois le trier par longueur de ligne, espaces compris. La commande suivante n'inclut pas d'espaces, y a-t-il un moyen de la modifier pour qu'elle fonctionne pour moi?
cat $@ | awk '{ print length, $0 }' | sort -n | awk '{$1=""; print $0}'
Réponses:
Répondre
Ou, pour faire votre sous-tri original (peut-être involontaire) de toutes les lignes de longueur égale:
Dans les deux cas, nous avons résolu votre problème déclaré en nous éloignant de awk pour votre coupe finale.
Lignes de longueur correspondante - que faire en cas d'égalité:
La question ne précisait pas si un autre tri était souhaité ou non pour les lignes de longueur correspondante. J'ai supposé que c'était indésirable et j'ai suggéré l'utilisation de
-s
(--stable
) pour éviter que de telles lignes soient triées les unes par rapport aux autres, et les conserver dans l'ordre relatif dans lequel elles apparaissent dans l'entrée.(Ceux qui veulent plus de contrôle sur le tri de ces liens pourraient envisager l'
--key
option de tri .)Pourquoi la tentative de solution de la question échoue (reconstruction de ligne awk):
Il est intéressant de noter la différence entre:
Ils cèdent respectivement
La section pertinente du manuel de (gawk) mentionne seulement en aparté que awk va reconstruire la totalité de $ 0 (basé sur le séparateur, etc.) lorsque vous modifiez un champ. Je suppose que ce n'est pas un comportement fou. Il a ceci:
"Enfin, il y a des moments où il est pratique de forcer awk à reconstruire l'intégralité de l'enregistrement, en utilisant la valeur actuelle des champs et OFS. Pour ce faire, utilisez l'affectation apparemment anodine:"
"Cela oblige awk à reconstruire le disque."
Entrée de test comprenant quelques lignes de même longueur:
la source
cat $@
c'est cassé aussi. Vous voulez absolument le citer, commecat "$@"
La solution AWK de neillb est idéale si vous voulez vraiment l'utiliser
awk
et elle explique pourquoi c'est un problème, mais si vous voulez faire le travail rapidement et que vous ne vous souciez pas de ce que vous faites, une solution est d'utilisersort()
Fonction de Perl avec une routine de caparison personnalisée pour parcourir les lignes d'entrée. Voici une seule ligne:Vous pouvez le mettre dans votre pipeline là où vous en avez besoin, soit en recevant STDIN (de
cat
ou une redirection shell) ou simplement donner le nom de fichier à perl comme un autre argument et le laisser ouvrir le fichier.Dans mon cas, j'avais d'abord besoin des lignes les plus longues, alors j'ai échangé
$a
et$b
dans la comparaison.la source
cat testfile.txt | perl -e 'print sort { length($a) <=> length($b) } <>' > out.txt
type testfile.txt | perl -e "print sort { length($a) <=> length($b) } <>" > out.txt
Essayez plutôt cette commande:
la source
Résultats de référence
Vous trouverez ci-dessous les résultats d'un benchmark des solutions issues d'autres réponses à cette question.
Méthode d'essai
Résultats
perl
solution de Caleb a pris 11,2 secondesperl
solution a pris 11,6 secondesawk
solution n ° 1 de neillb a pris 20 secondesawk
solution n ° 2 de neillb a pris 23 secondesawk
solution d'Anubhava a pris 24 secondesawk
solution de Jonathan a pris 25 secondesbash
solution de Fretz prend 400 fois plus de temps que lesawk
solutions (en utilisant un cas de test tronqué de 100 000 lignes). Cela fonctionne bien, cela prend juste une éternité.perl
Option supplémentaireDe plus, j'ai ajouté une autre solution Perl:
la source
Pure Bash:
la source
La
length()
fonction inclut des espaces. Je ne ferais que des ajustements mineurs à votre pipeline (y compris en évitant UUOC ).La
sed
commande supprime directement les chiffres et les deux points ajoutés par laawk
commande. Vous pouvez également conserver votre mise en formeawk
:la source
J'ai trouvé que ces solutions ne fonctionneront pas si votre fichier contient des lignes commençant par un nombre, car elles seront triées numériquement avec toutes les lignes comptées. La solution est de donner
sort
le-g
drapeau (general-numeric-sort) au lieu de-n
(numeric-sort):la source
-n
à votre suggérée-g
entraînerait une amélioration, donc je ne m'attends pas à ce que ce soit le cas. J'ai maintenant abordé, dans ma réponse, comment interdire le sous-tri des lignes de longueur égale (en utilisant--stable
). Que ce soit ce que vous vouliez dire ou non, merci de l'avoir porté à mon attention! J'ai également ajouté une entrée considérée pour tester.awk
pièce générera une liste de lignes précédées d'une longueur de ligne et d'un espace. La tuyauteriesort -n
fonctionnera comme prévu. Mais si l'une de ces lignes a déjà un nombre au début, ces lignes commenceront par longueur + espace + nombre.sort -n
ne tient pas compte de cet espace et le traitera comme un nombre concaténé de longueur + nombre. L'utilisation de l'-g
indicateur s'arrêtera à la place au premier espace, donnant un tri correct. Essayez-le vous-même en créant un fichier avec des lignes numérotées et exécutez la commande étape par étape.sort -n
ne tient pas compte de l'espace et produit un tri incorrect.sort -g
produit le bon ordre.-n
danssort (GNU coreutils) 8.21
. Lainfo
documentation décrit-g
comme moins efficace et potentiellement moins précise (elle convertit les nombres en flottants), donc ne l'utilisez probablement pas si vous n'en avez pas besoin.-n
: "Trier numériquement. Le nombre commence chaque ligne et se compose de blancs facultatifs, d'un signe optionnel '-' et de zéro ou plusieurs chiffres éventuellement séparés par des milliers de séparateurs, éventuellement suivis d'un caractère décimal et zéro ou plusieurs chiffres . Un nombre vide est traité comme '0'. La locale 'LC_NUMERIC' spécifie le caractère décimal et le séparateur des milliers. Par défaut, un espace est un espace ou une tabulation, mais la locale 'LC_CTYPE' peut changer cela. "Avec POSIX Awk:
Exemple
la source
1) solution pure awk. Supposons que la longueur de la ligne ne puisse pas être supérieure à 1024 alors
nom de fichier de chat | awk 'BEGIN {min = 1024; s = "";} {l = longueur (0 $); si (l <min) {min = l; s = $ 0;}} END {print s} '
2) une solution de bash de ligne en supposant que toutes les lignes n'ont qu'un seul mot, mais peut être retravaillée pour tous les cas où toutes les lignes ont le même nombre de mots:
LINES = $ (nom de fichier cat); pour k dans $ LINES; faire printf "$ k"; echo $ k | wc -L; fait | sort -k2 | tête -n 1 | couper -d "" -f1
la source
Voici une méthode compatible multi-octets de tri des lignes par longueur. Cela demande:
wc -m
est disponible pour vous (macOS l'a).LC_ALL=UTF-8
. Vous pouvez le définir soit dans votre .bash_profile, soit simplement en l'ajoutant avant la commande suivante.testfile
a un encodage de caractères correspondant à votre locale (par exemple, UTF-8).Voici la commande complète:
Expliquer partie par partie:
l=$0; gsub(/\047/, "\047\"\047\"\047", l);
← fait une copie de chaque ligne dans la variable awkl
et double-échappe chaque'
pour que la ligne puisse être renvoyée en toute sécurité comme une commande shell (\047
est un guillemet simple en notation octale).cmd=sprintf("echo \047%s\047 | wc -m", l);
← c'est la commande que nous exécuterons, qui fait écho à la ligne échappéewc -m
.cmd | getline c;
← exécute la commande et copie la valeur du nombre de caractères renvoyée dans la variable awkc
.close(cmd);
← fermez le tube à la commande shell pour éviter d'atteindre une limite système sur le nombre de fichiers ouverts dans un processus.sub(/ */, "", c);
← supprime l'espace blanc de la valeur du nombre de caractères renvoyée parwc
.{ print c, $0 }
← imprime la valeur du nombre de caractères de la ligne, un espace et la ligne d'origine.| sort -ns
← trie les lignes (par valeurs de nombre de caractères préfixées) numériquement (-n
) et maintient un ordre de tri stable (-s
).| cut -d" " -f2-
← supprime les valeurs de nombre de caractères ajoutées au début.C'est lent (seulement 160 lignes par seconde sur un Macbook Pro rapide) car il doit exécuter une sous-commande pour chaque ligne.
Sinon, faites-le uniquement avec
gawk
(à partir de la version 3.1.5, gawk est compatible multi-octets), ce qui serait beaucoup plus rapide. C'est beaucoup de mal à faire toutes les échappements et les doubles guillemets pour passer en toute sécurité les lignes à travers une commande shell de awk, mais c'est la seule méthode que j'ai pu trouver qui ne nécessite pas l'installation de logiciel supplémentaire (gawk n'est pas disponible par défaut sur macOS).la source