2009-11-03 7 views
4

J'ai un grand ensemble (600 impair) de termes de recherche et de remplacement que je dois exécuter en tant que script sed sur certains fichiers. Le problème est que les termes de recherche ne sont PAS orthogonaux ... mais je pense que je peux m'en tirer en triant par longueur de ligne (c'est-à-dire en retirant les plus longues correspondances, puis alphabétiquement dans chaque longueur.Comment trier par longueur de ligne, puis inverser alphabétiquement

aaba 
aa 
ab 
abba 
bab 
aba 

ce que je veux est trié fixé comme:

abba 
aaba 
bab 
aba 
ab 
aa 

est-il possible de le faire en dire préfixer la longueur de la ligne et le tri par un champ

Pour bonus? des notes :-) !!! La recherche et le remplacer est en fait tout simplement un cas de remplacement terme avec _term_ et le code sed j'allais utiliser était s/term/_term_/g Comment puis-je écrire l'expression rationnelle pour éviter de remplacer les termes déjà à l'intérieur _ paires?

Répondre

2

Vous pouvez compacter tout en un seul regexp:

$ sed -e 's/\(aaba\|aa\|abba\)/_\1_/g' 
testing words aa, aaba, abba. 
testing words _aa_, _aaba_, _abba_. 

Si je comprends bien votre question, cela résoudra tous vos problèmes: pas de "double remplacement" et toujours correspondre au mot le plus long.

+0

Ne devriez-vous toujours pas trier les articles par longueur? Ou y aura-t-il une sorte de match gourmand qui correspondra toujours à la chaîne la plus longue possible? – mob

+0

... en plus, c'est une ligne longue pour 600 items ;-) mais peut-être je peux la diviser en plus de lignes ... – Dycey

+2

Pas besoin de ça: Une expression régulière trouvera toujours la plus longue correspondance. –

0

Cela triera un fichier par la longueur de la ligne, les lignes les plus longues premières:

cat file.txt | (while read LINE; do echo -e "${#LINE}\t$LINE"; done) | sort -rn | cut -f 2- 

Cela remplacera term avec _term_ mais ne tourne pas _term_ en __term__:

sed -r 's/(^|[^_])term([^_]|$)/\1_term_\2/g' 
sed -r -e 's/(^|[^_])term/\1_term_/g' -e 's/term([^_]|$)/_term_\1/g' 

La première volonté fonctionne plutôt bien, sauf qu'il manquera _term et term_, laissant par erreur ceux qui sont seuls. Utilisez le second si c'est important. Voici mon cas de test stupide:

# echo here is _term_ and then a term you terminator haha _terminator and then _term_inator term_inator | sed -re 's/(^|[^_])term([^_]|$)/\1_term_\2/g' 
here is _term_ and then a _term_ you _term_inator haha _terminator and then _term_inator term_inator 
# echo here is _term_ and then a term you terminator haha _terminator and then _term_inator term_inator | sed -r -e 's/(^|[^_])term/\1_term_/g' -e 's/term([^_]|$)/_term_\1/g' 
here is _term_ and then a _term_ you _term_inator haha __term_inator and then _term_inator _term__inator 
+0

parfait! Je vais essayer! – Dycey

1

Juste tuyau votre flux à travers ce type de script:

#!/usr/bin/python 
import sys 

all={} 
for line in sys.stdin: 
    line=line.rstrip() 
    if len(line) in all: 
     all[len(line)].append(line) 
    else: 
     all[len(line)]=[line] 

for l in reversed(sorted(all)): 
    print "\n".join(reversed(sorted(all[l]))) 

Et pour la marque bonus question: Encore une fois, faites-le en python (à moins qu'il y est vraiment une raison pas, mais je serais assez curieux de savoir)

+0

Est-ce le moyen le plus court ou le plus clair de faire ça, en Python? –

+0

peut-être pas; C'était ma première pensée. – Gyom

+0

Personnellement, c'est assez rapide et assez sale que je préfère utiliser un Perl one-liner que d'écrire un script Python entier. Bien que si vous insistez sur Python, il pourrait être plus propre (si moins efficace) de juste slurp le fichier, puis le trier, puis le cracher. –

0

Cela ne la sorte par la longueur, puis inverse alpha bits

for mask in `tr -c "\n" "." < $FILE | sort -ur` 
do 
    grep "^$mask$" $FILE | sort -r 
done 

L'utilisation tr remplace chaque caractère $FILE avec une période - qui correspond à un seul caractère grep.

10

Vous pouvez le faire dans une ligne de script Perl:

perl -e 'print sort { length $b<=>length $a || $b cmp $a } <>' input 
+0

Devrait probablement changer '$ a cmp $ b' pour être' $ b cmp $ a', puisqu'il le voulait dans l'ordre inverse. –

+0

Merci Brad, corrigé. – mob

+2

+1 Toute tâche pour laquelle vous utilisez beaucoup de script shell peut être plus facile, plus courte et potentiellement plus claire dans Perl. –

2
$ awk '{print length($1),$1}' file |sort -rn 
4 abba 
4 aaba 
3 bab 
3 aba 
2 ab 
2 aa 

je vous laisse essayer de se débarrasser de la première colonne vous

Questions connexes