Bir boru hattında birden fazla tr işlemini önlemek için tr komutları zincirlenebilir mi?


11

Ben txt dosyaları bir demet var, ben onları daha düşük kasalı, sadece alfabetik ve satır başına bir kelime çıkışı istiyorum, böyle trbir boru hattı birkaç komutları ile yapabilirsiniz :

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

Bunu tek bir taramada yapmak mümkün mü? Bunu yapmak için bir C programı yazmak olabilir, ama hissediyorum kullanarak bunu yapmanın bir yolu var gibi tr, sed, awkveya perl.


Hangi işletim sistemini kullanıyorsunuz? GNU araçlarına erişiminiz var mı?
terdon

Yanıtlar:


9

Birden çok çeviriyi birleştirebilirsiniz (çakışan yerel ayara bağlı kümeleri içeren karmaşık durumlar hariç), ancak silme işlemini çeviriyle birleştiremezsiniz.

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

İki trçağrının daha karmaşık araçlara yapılan tek bir çağrıdan daha hızlı olması muhtemeldir, ancak bu, giriş boyutuna, farklı karakterlerin oranlarına tr, işletim sisteminin, sayıdaki uygulamanın ve rakip araçların sayısına bağlıdır. çekirdekler, vb.


Yeniden birleştirmekten emin değilimtr -s '[:upper:] [:punct:]' '[:lower:]\n' <doyle_sherlock_holmes.txt
Costas

1
@Costas Bu noktalama işaretlerini yeni satırlara dönüştürür. Bu özel uygulama için uygun olabilir, ancak çıktı orijinal ile aynı değildir.
Gilles 'SO- kötü olmayı bırak'

@Costas - Newline olayı burada kabul edilebilir olsa da, büyük harflerin sıkılmasını düşünmüyorum. Örneğin: printf 'A.AAAA,A' | tr -s '[:upper:] [:punct:]' '[:lower:][\n*]'alır a\na\na've dönüşümü ... '[:lower:]\n'her '[:punct:]'durumda bir şey yapmayabilir - bazı trs set1'i 2'ye eşleştirecek ve bazıları zımni yapacaktır [\n*]. Buradaki aralığı kullanmak daha iyidir.
mikeserv

4

İşte birkaç yaklaşım:

  • GNU grepve tr: tüm kelimeleri bul ve küçük harfe çevir

    grep -Po '\w+' file | tr '[A-Z]' '[a-z]'
  • GNU grep ve perl: yukarıdaki gibi fakat perl küçük harfe dönüşümü ele alıyor

    grep -Po '\w+' file | perl -lne 'print lc()'
  • perl: tüm alfabetik karakterleri bulun ve küçük harflerle yazdırın (teşekkürler @steeldriver):

    perl -lne 'print lc for /[a-z]+/ig' file
  • sed: alfabetik olmayan veya boşluk olmayan tüm karakterleri kaldırın, tüm alfabetik karakterleri küçük harfli sürümlerle değiştirin ve tüm boşlukları yeni satırlarla değiştirin. Bunun tüm boşlukların boşluk olduğunu, sekme olmadığını varsaydığını unutmayın.

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

2
Bir şey perl -lne 'print lc for /[[:alpha:]]+/g'de işe yarar mı? yoksa kötü bir tarz mı? (Ben perl için yeni ve öğrenmeye çalışıyorum!)
steeldriver

@steeldriver evet, güzel bir tane! Perl öğreniyorsanız, eminim onun sloganına rastladınız: TMTOWTDI :) Teşekkürler, bunu ekleyeceğim.
terdon

3
Yeni sürüm ile (> 4.2.1)sed -z 's/\W*\(\w\+\)\W*/\L\1\n/g'
Costas

@Costas ah, şimdi sedyapabilir \wmisin? Güzel!
terdon

@terdon - Kostas bundan bahsetmedi çünkü bir süre için değil, ben yukarıdaki yorum hakkında en ilginç şey, GNU olduğunu düşünüyorum bitti sed'ın -zbu döngüleri boyunca - ero sınırlandırmaktadır anahtarı \0NULsatırbaşıyla ziyade bu. Gibi bir şey yaptığınızda oldukça serin tar -c . | tr -s \\0 | sed -z ...- ama biraz yavaş.
mikeserv

4

Evet. Bunu trbir ASCII yerel ayarında yapabilirsiniz ( tryine de bir GNU için , bu türün tek amacı) . POSIX sınıflarını kullanabilir veya her karakterin bayt değerlerine sekizli sayı ile başvurabilirsiniz. Dönüşümlerini aralıklara bölebilirsiniz.

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

Yukarıdaki komut tüm büyük harfleri küçük harfe dönüştürür, küçük harf karakterlerini tamamen yok sayar ve diğer tüm karakterleri yeni satırlara dönüştürür. Tabii ki, o zaman bir ton boş çizgi ile sarılıyorsunuz. tr -sQueeze tekrarlar bu durumda yararlı olabilir geçiş, ancak yanında kullanırsanız [:upper:]için [:lower:]daha sonra dönüştürme size sıra küçük karakterlere sıkma rüzgar. Bu şekilde hala ikinci bir filtre gerektirir ...

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

...veya...

LC... tr ... | grep .

... ve bu yüzden yapmaktan çok daha az kullanışlı oluyor ...

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

... -calfabetik karakterlerin dizilimini sırayla tek bir yeni satıra bir parçaya sıkıştırarak borunun diğer tarafında yukarıdan aşağıya dönüşümü yapar.

Bu, o doğanın aralıklarının yararlı olmadığı anlamına gelmez. Gibi şeyler:

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

... giriş baytlarını değerlerinin yayılı spektrumu üzerindeki tüm rakamlara dönüştürdüğü için oldukça kullanışlı olabilir. İsraf etme, istemiyorum, biliyorsun.

Dönüşümü yapmanın başka bir yolu da içerebilir 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

Çünkü ddhem yapabilir unblockve lcaseaynı zamanda dönüşümleri, hatta kendisine işten çok geçmek mümkün olabilir. Ancak bu, kelime başına bayt sayısını doğru bir şekilde tahmin edebiliyorsanız veya en azından her kelimeyi önceden tahmin edilebilir bir bayt sayısına boşluklarla doldurabiliyorsanız gerçekten yararlı olabilir, çünkü unblockher bloğun sonunda arka boşluklar yer.



@TobiLehman - Onayladığınız için çok mutluyum.
mikeserv
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.