I comandi tr possono essere concatenati per evitare più processi tr in una pipeline?


11

Ho un sacco di file txt, mi piacerebbe inviarli in minuscolo, solo alfabetici e una parola per riga, posso farlo con diversi trcomandi in una pipeline come questa:

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

È possibile farlo in una scansione? Potrei scrivere un programma in C per fare questo, ma mi sento come se ci fosse un modo per farlo utilizzando tr, sed, awko perl.


Quale sistema operativo stai usando? Hai accesso agli strumenti GNU?
terdon

Risposte:


9

È possibile combinare più traduzioni (ad eccezione di casi complessi che coinvolgono insiemi dipendenti dalla locale sovrapposti), ma non è possibile combinare la cancellazione con la traduzione.

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

trÈ probabile che due chiamate siano più veloci di una singola chiamata a strumenti più complessi, ma ciò dipende molto dalle dimensioni dell'input, dalle proporzioni di caratteri diversi, dall'implementazione tre dagli strumenti concorrenti, dal sistema operativo, dal numero di anime, ecc.


Non sono sicuro di combinaretr -s '[:upper:] [:punct:]' '[:lower:]\n' <doyle_sherlock_holmes.txt
Costas l'

1
@Costas Questo trasformerebbe la punteggiatura in newline. Potrebbe essere ok per questa particolare applicazione, ma l'output non è lo stesso dell'originale.
Gilles 'SO- smetti di essere malvagio' l'

@Costas - mentre la cosa newline potrebbe essere accostabile qui, non penso che stringere i caratteri maiuscoli sarebbe. Ad esempio: printf 'A.AAAA,A' | tr -s '[:upper:] [:punct:]' '[:lower:][\n*]'ottiene a\na\na', e la trasformazione per ... '[:lower:]\n'potrebbe non fare assolutamente nulla in '[:punct:]'alcun modo - alcuni trs tronceranno set1 in modo che corrisponda a 2 e alcuni faranno un implicito [\n*]. È meglio solo usare la gamma lì.
Mikeserv,

4

Ecco alcuni approcci:

  • GNU grepe tr: trova tutte le parole e rendile minuscole

    grep -Po '\w+' file | tr '[A-Z]' '[a-z]'
  • GNU grep e perl: come sopra ma perl gestisce la conversione in minuscolo

    grep -Po '\w+' file | perl -lne 'print lc()'
  • perl: trova tutti i caratteri alfabetici e stampali in minuscolo (grazie a @steeldriver):

    perl -lne 'print lc for /[a-z]+/ig' file
  • sed: rimuove tutti i caratteri non alfabetici o gli spazi, sostituisce tutti i caratteri alfabetici con le loro versioni minuscole e sostituisce tutti gli spazi con nuove righe. Si noti che ciò presuppone che tutti gli spazi siano spazi, nessuna scheda.

    sed 's/[^a-zA-Z ]\+//g;s/[a-zA-Z]\+/\L&/g; s/ \+/\n/g' file

2
Funzionerebbe qualcosa del genere perl -lne 'print lc for /[[:alpha:]]+/g'? o è stile povero? (Sono nuovo di Perù e sto cercando di imparare!)
steeldriver,

@steeldriver sì, bello! Se stai imparando il Perl, sono sicuro che hai trovato il suo motto: TMTOWTDI :) Grazie, lo aggiungerò.
terdon

3
Con la nuova versione (> 4.2.1)sed -z 's/\W*\(\w\+\)\W*/\L\1\n/g'
Costas,

@Costas ah, ora sedpuoi farlo \w? Freddo!
terdon

@terdon - è fatto che per un po ', ma, a causa Costas non ne ha parlato, credo che la cosa più interessante il commento di cui sopra è GNU seds' -zero delimitare switch - passa ciclicamente nel corso \0NULs piuttosto che a capo. Abbastanza bello quando fai qualcosa del genere tar -c . | tr -s \\0 | sed -z ...- ma un po 'lento.
Mikeserv,

4

Sì. Puoi farlo w / trin una localizzazione ASCII (che è, per una GNU trcomunque, tipo della sua unica competenza) . È possibile utilizzare le classi POSIX oppure è possibile fare riferimento ai valori di byte di ciascun carattere per numero ottale. Puoi anche dividere le loro trasformazioni su diversi intervalli.

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

Il comando sopra trasformerebbe tutti i caratteri maiuscoli in minuscoli, ignorerebbe completamente i caratteri minuscoli e trasformerebbe tutti gli altri caratteri in newline. Certo, poi finisci con un sacco di righe vuote. L' tr -sopzione Queeze Repeats potrebbe essere utile in quel caso, ma se lo usi insieme [:upper:]alla [:lower:]trasformazione, finirai anche per stringere i caratteri maiuscoli. In questo modo richiede ancora un secondo filtro come ...

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

...o...

LC... tr ... | grep .

... e quindi finisce per essere molto meno conveniente rispetto a fare ...

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

... che comprime il -ccomplemento di caratteri alfabetici in sequenza in una singola riga nuova di un pezzo, quindi la parte superiore in basso si trasforma sull'altro lato del tubo.

Ciò non significa che intervalli di quella natura non siano utili. Cose come:

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

... può essere molto utile in quanto converte i byte di input in tutte le cifre in uno spettro diffuso dei loro valori. Non sprecare, non vuoi, lo sai.

Un altro modo per effettuare la trasformazione potrebbe comportare 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

Perché ddpuò fare entrambe unblocke lcaseconversioni allo stesso tempo, potrebbe anche essere possibile passare gran parte del lavoro fuori di esso. Ma ciò può essere davvero utile solo se puoi prevedere con precisione il numero di byte per parola - o almeno puoi riempire prima ogni parola di spazi con un conteggio di byte prevedibile, perché unblockmangia spazi finali alla fine di ogni blocco.


+2 punti bonus per essere ddcoinvolti :)
tlehman,

@TobiLehman - Mi fa molto piacere che tu approvi.
Mikeserv,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.