Les commandes tr peuvent-elles être chaînées pour éviter plusieurs processus tr dans un pipeline?

11

J'ai un tas de fichiers txt, je voudrais les sortir en minuscules, seulement alphabétique et un mot par ligne, je peux le faire avec plusieurs trcommandes dans un pipeline comme ceci:

tr -d '[:punct:]' <doyle_sherlock_holmes.txt | tr '[:upper:]' '[:lower:]' | tr ' ' '\n'

Est-il possible de le faire en une seule numérisation? Je pourrais écrire un programme C pour le faire, mais je me sens comme il y a une façon de le faire en utilisant tr, sed, awkou perl.

tlehman
la source
Quel système d'exploitation utilisez-vous? Avez-vous accès aux outils GNU?
terdon

Réponses:

9

Vous pouvez combiner plusieurs traductions (à l'exception des cas complexes impliquant des ensembles qui dépendent des paramètres régionaux qui se chevauchent), mais vous ne pouvez pas combiner la suppression avec la traduction.

<doyle_sherlock_holmes.txt tr -d '[:punct:]' | tr '[:upper:] ' '[:lower:]\n'

Deux appels à trsont susceptibles d'être plus rapides qu'un seul appel à des outils plus complexes, mais cela dépend beaucoup de la taille d'entrée, des proportions de différents caractères, de la mise en œuvre tret des outils concurrents, du système d'exploitation, du nombre de noyaux, etc.

Gilles 'SO- arrête d'être méchant'
la source
Je ne suis pas sûr de combinertr -s '[:upper:] [:punct:]' '[:lower:]\n' <doyle_sherlock_holmes.txt
Costas
1
@Costas Cela transformerait la ponctuation en nouvelles lignes. Cela peut être correct pour cette application particulière, mais la sortie n'est pas la même que l'original.
Gilles 'SO- arrête d'être méchant'
@Costas - bien que la nouvelle ligne puisse être acceptée ici, je ne pense pas que presser les caractères majuscules serait. Par exemple: printf 'A.AAAA,A' | tr -s '[:upper:] [:punct:]' '[:lower:][\n*]'gets a\na\na', et la transformation pour ... '[:lower:]\n'pourrait ne pas nécessairement faire quoi que ce soit de toute '[:punct:]'façon - certains trs tronqueront set1 pour correspondre à 2 et d'autres feront implicitement [\n*]. Il vaut mieux simplement utiliser la gamme là-bas.
mikeserv
4

Voici quelques approches:

  • GNU grepet tr: trouver tous les mots et les mettre en minuscules

    grep -Po '\w+' file | tr '[A-Z]' '[a-z]'
  • GNU grep et perl: comme ci-dessus mais perl gère la conversion en minuscules

    grep -Po '\w+' file | perl -lne 'print lc()'
  • perl: recherchez tous les caractères alphabétiques et imprimez-les en minuscules (merci @steeldriver):

    perl -lne 'print lc for /[a-z]+/ig' file
  • sed: supprimez tous les caractères non alphabétiques ou espaces, remplacez tous les caractères alphabétiques par leurs versions minuscules et remplacez tous les espaces par des retours à la ligne. Notez que cela suppose que tous les espaces blancs sont des espaces, pas d'onglets.

    sed 's/[^a-zA-Z ]\+//g;s/[a-zA-Z]\+/\L&/g; s/ \+/\n/g' file
terdon
la source
2
Est-ce que quelque chose aimerait perl -lne 'print lc for /[[:alpha:]]+/g'aussi fonctionner? ou est-ce un mauvais style? (Je suis nouveau sur Perl et j'essaie d'apprendre!)
Steeldriver
@steeldriver oui, ce serait bien! Si vous apprenez Perl, je suis sûr que vous avez rencontré sa devise: TMTOWTDI :) Merci, je vais ajouter celle-là.
terdon
3
Avec une nouvelle version (> 4.2.1)sed -z 's/\W*\(\w\+\)\W*/\L\1\n/g'
Costas
@Costas ah, sedpeut faire \wmaintenant? Cool!
terdon
@terdon - cela a été fait pendant un certain temps, mais, parce que Costas ne l'a pas mentionné, je pense que la chose la plus intéressante à propos du commentaire ci-dessus est sedle -zcommutateur ero delimit de GNU - il passe sur \0NULs plutôt que sur les nouvelles lignes. Assez cool quand vous faites quelque chose comme tar -c . | tr -s \\0 | sed -z ...- mais un peu lent.
mikeserv
4

Oui. Vous pouvez le faire avec trune locale ASCII (qui est, pour un GNU de trtoute façon, une sorte de sa seule compétence) . Vous pouvez utiliser les classes POSIX ou référencer les valeurs d'octets de chaque caractère par nombre octal. Vous pouvez également répartir leurs transformations sur plusieurs plages.

LC_ALL=C tr '[:upper:]\0-\101\133-140\173-\377' '[:lower:][\n*]' <input

La commande ci-dessus transformerait tous les caractères majuscules en minuscules, ignorerait complètement les caractères minuscules et transformerait tous les autres caractères en nouvelles lignes. Bien sûr, vous vous retrouvez avec une tonne de lignes blanches. Le tr -scommutateur de répétitions queeze pourrait être utile dans ce cas, mais si vous l'utilisez à côté de la transformation [:upper:]to, [:lower:]vous finissez également par presser les caractères majuscules. De cette façon, il nécessite toujours un deuxième filtre comme ...

LC... tr ... | tr -s \\n

...ou...

LC... tr ... | grep .

... et donc ça devient beaucoup moins pratique que de le faire ...

LC_ALL=C tr -sc '[:alpha:]' \\n <input | tr '[:upper:]' '[:lower:]'

... qui comprime le -ccomplément de caractères alphabétiques par séquence en une seule nouvelle ligne d'un morceau, puis transforme le haut en bas de l'autre côté du tuyau.

Cela ne veut pas dire que des plages de cette nature ne sont pas utiles. Choses comme:

tr '\0-\377' '[1*25][2*25][3*25][4*25][5*25][6*25][7*25][8*25][9*25][0*]' </dev/random

... peut être assez pratique car il convertit les octets d'entrée en tous les chiffres sur un spectre étalé de leurs valeurs. Ne gaspillez pas, ne voulez pas, vous savez.

Une autre façon de faire la transformation pourrait impliquer dd.

tr '\0-\377' '[A*64][B*64][C*64][D*64]' </dev/urandom |
dd bs=32 cbs=8 conv=unblock,lcase count=1

dadbbdbd
ddaaddab
ddbadbaa
bdbdcadd

Parce qu'il ddpeut faire les deux unblocket les lcaseconversions en même temps, il pourrait même être possible de lui passer une grande partie du travail. Mais cela ne peut être vraiment utile que si vous pouvez prédire avec précision le nombre d'octets par mot - ou au moins vous pouvez remplir chaque mot avec des espaces à l'avance pour un nombre d'octets prévisible, car unblockmange des espaces de fin à la fin de chaque bloc.

mikeserv
la source
+2 points bonus pour dds'impliquer :)
tlehman
@TobiLehman - Je suis très heureux que vous approuviez.
mikeserv