Dapatkah perintah tr dirantai untuk menghindari beberapa proses tr dalam pipa?

11

Saya memiliki banyak file txt, saya ingin menampilkannya dengan huruf kecil, hanya alfabet dan satu kata per baris, saya bisa melakukannya dengan beberapa trperintah dalam pipa seperti ini:

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

Apakah mungkin melakukan ini dalam satu pemindaian? Aku bisa menulis sebuah program C untuk melakukan hal ini, tapi aku merasa seperti ada cara untuk melakukannya dengan menggunakan tr, sed, awkatau perl.

tlehman
sumber
OS apa yang Anda gunakan? Apakah Anda memiliki akses ke alat GNU?
terdon

Jawaban:

9

Anda dapat menggabungkan beberapa terjemahan (kecuali kasus kompleks yang melibatkan set yang bergantung pada lokal yang tumpang tindih), tetapi Anda tidak dapat menggabungkan penghapusan dengan terjemahan.

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

Dua panggilan ke trcenderung lebih cepat daripada satu panggilan ke alat yang lebih kompleks, tetapi ini sangat tergantung pada ukuran input, pada proporsi karakter yang berbeda, pada implementasi trdan alat yang bersaing, pada sistem operasi, pada nomor inti, dll.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
Saya tidak yakin menggabungkan kembalitr -s '[:upper:] [:punct:]' '[:lower:]\n' <doyle_sherlock_holmes.txt
Costas
1
@Costas Itu akan mengubah tanda baca menjadi baris baru. Mungkin ok untuk aplikasi khusus ini, tetapi hasilnya tidak sama dengan aslinya.
Gilles 'SO- stop being evil'
@ Costas - sementara baris baru mungkin dapat diterima di sini, saya tidak berpikir menekan huruf besar akan menjadi. Sebagai contoh: printf 'A.AAAA,A' | tr -s '[:upper:] [:punct:]' '[:lower:][\n*]'dapatkan a\na\na', dan transformasi untuk ... '[:lower:]\n'mungkin tidak perlu melakukan apa pun untuk '[:punct:]'tetap - beberapa trs akan memotong set1 untuk mencocokkan 2 dan beberapa akan melakukan yang tersirat [\n*]. Lebih baik hanya menggunakan rentang di sana.
mikeserv
4

Berikut ini beberapa pendekatan:

  • GNU grepdan tr: temukan semua kata dan buat huruf kecil

    grep -Po '\w+' file | tr '[A-Z]' '[a-z]'
  • GNU grep dan perl: seperti di atas tetapi perl menangani konversi ke huruf kecil

    grep -Po '\w+' file | perl -lne 'print lc()'
  • perl: temukan semua karakter alfabet dan cetak dalam huruf kecil (terima kasih @steeldriver):

    perl -lne 'print lc for /[a-z]+/ig' file
  • sed: hapus semua karakter yang bukan alfabet atau spasi, gantikan semua karakter alfabet dengan versi huruf kecilnya dan ganti semua spasi dengan baris baru. Perhatikan bahwa ini mengasumsikan bahwa semua spasi putih adalah spasi, tidak ada tab.

    sed 's/[^a-zA-Z ]\+//g;s/[a-zA-Z]\+/\L&/g; s/ \+/\n/g' file
terdon
sumber
2
Apakah sesuatu seperti perl -lne 'print lc for /[[:alpha:]]+/g'juga berfungsi? atau itu gaya yang buruk? (Saya baru perl dan mencoba belajar!)
steeldriver
@steeldriver ya itu akan, bagus! Jika Anda mempelajari Perl, saya yakin Anda telah menemukan moto: TMTOWTDI :) Terima kasih, saya akan menambahkan itu.
terdon
3
Dengan versi baru (> 4.2.1)sed -z 's/\W*\(\w\+\)\W*/\L\1\n/g'
Costas
@Costas ah, sedbisa lakukan \wsekarang? Keren!
terdon
@terdon - itu melakukan itu untuk sementara, tapi, karena Costas tidak menyebutkan itu, saya pikir hal yang paling menarik tentang komentar di atas adalah GNU sed's -zero membatasi switch - itu siklus lebih \0NULs daripada baris baru. Cukup keren ketika Anda melakukan sesuatu seperti tar -c . | tr -s \\0 | sed -z ...- tetapi agak lambat.
mikeserv
4

Iya. Anda dapat melakukannya trdengan lokal ASCII (yang, untuk GNU tr, jenis dari satu-satunya ruang lingkupnya) . Anda bisa menggunakan kelas POSIX, atau Anda bisa mereferensikan nilai byte dari setiap karakter dengan angka oktal. Anda dapat membagi transformasi mereka di seluruh rentang, juga.

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

Perintah di atas akan mengubah semua karakter huruf besar menjadi huruf kecil, mengabaikan karakter huruf kecil seluruhnya, dan mengubah semua karakter lain menjadi baris baru. Tentu saja, maka Anda berakhir dengan satu ton garis kosong. The tr -smengulangi queeze beralih dapat berguna dalam kasus itu, tapi jika Anda menggunakannya di samping [:upper:]untuk [:lower:]transformasi maka Anda berakhir meremas karakter huruf besar juga. Dengan cara itu masih membutuhkan filter kedua seperti ...

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

...atau...

LC... tr ... | grep .

... Dan akhirnya menjadi jauh lebih nyaman daripada melakukan ...

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

... yang menekan -complement karakter alfabet secara berurutan menjadi satu baris baru, kemudian melakukan transformasi atas ke bawah di sisi lain pipa.

Itu bukan untuk mengatakan bahwa rentang sifat itu tidak berguna. Hal-hal seperti:

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

... bisa sangat berguna karena mengubah byte input ke semua digit pada spektrum spread nilai-nilainya. Buang tidak, mau tidak, kau tahu.

Cara lain untuk melakukan transformasi bisa melibatkan 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

Karena dddapat melakukan keduanya unblockdan lcasekonversi pada saat yang sama, bahkan mungkin untuk melewati banyak pekerjaan untuk itu. Tapi itu hanya bisa sangat berguna jika Anda dapat secara akurat memprediksi jumlah byte per kata - atau paling tidak dapat mengimbangi setiap kata dengan spasi sebelumnya ke jumlah byte yang dapat diprediksi, karena unblockmakan spasi tambahan di akhir setiap blok.

mikeserv
sumber
+2 poin bonus untuk ddterlibat :)
tlehman
@TobiLehman - Saya sangat senang Anda menyetujuinya.
mikeserv