คำสั่ง tr สามารถถูกโยงโซ่เพื่อหลีกเลี่ยงกระบวนการ tr หลายอย่างในไพพ์ไลน์หรือไม่?


11

ฉันมีไฟล์ txt มากมายฉันต้องการส่งออกตามลำดับที่ต่ำกว่ามีเพียงตัวอักษรและหนึ่งคำต่อบรรทัดฉันสามารถทำได้ด้วยtrคำสั่งต่าง ๆในขั้นตอนนี้:

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

สามารถทำได้ในการสแกนครั้งเดียวหรือไม่? ฉันสามารถเขียนโปรแกรม C ที่จะทำเช่นนี้ แต่ฉันรู้สึกเหมือนมีวิธีที่จะทำมันได้ใช้tr, sed, หรือawkperl


คุณใช้ระบบปฏิบัติการอะไร? คุณมีสิทธิ์เข้าถึงเครื่องมือ GNU หรือไม่?
terdon

คำตอบ:


9

คุณสามารถรวมการแปลหลายรายการ (ยกเว้นกรณีที่ซับซ้อนที่เกี่ยวข้องกับชุดที่ขึ้นอยู่กับสถานที่ทับซ้อนกัน) แต่คุณไม่สามารถรวมการลบกับการแปล

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

การโทรสองครั้งtrมีแนวโน้มว่าจะเร็วกว่าการโทรครั้งเดียวไปยังเครื่องมือที่ซับซ้อนมากขึ้น แต่ขึ้นอยู่กับขนาดอินพุตในสัดส่วนของอักขระที่แตกต่างกันการใช้งานtrและเครื่องมือการแข่งขันบนระบบปฏิบัติการกับจำนวน ของแกน ฯลฯ


ฉันไม่แน่ใจว่าจะรวมกันอีกครั้งtr -s '[:upper:] [:punct:]' '[:lower:]\n' <doyle_sherlock_holmes.txt
Costas

1
@Costas ที่จะเปลี่ยนเครื่องหมายวรรคตอนเป็นบรรทัดใหม่ มันอาจจะโอเคสำหรับแอปพลิเคชั่นเฉพาะนี้ แต่เอาท์พุทจะไม่เหมือนเดิม
Gilles 'SO- หยุดความชั่วร้าย'

@Costas - ในขณะที่สิ่งที่ขึ้นบรรทัดใหม่อาจเป็นที่รู้จักที่นี่ฉันไม่คิดว่าจะบีบตัวอักษรตัวพิมพ์ใหญ่ ตัวอย่างเช่นprintf 'A.AAAA,A' | tr -s '[:upper:] [:punct:]' '[:lower:][\n*]'ได้รับa\na\na'และการเปลี่ยนแปลงสำหรับ... '[:lower:]\n'อาจไม่จำเป็นต้องทำอะไรเลยที่จะ'[:punct:]'ไป - บางtrs จะตัด Set1 เพื่อให้ตรงกับ 2 [\n*]และบางส่วนจะทำโดยนัย มันดีกว่าที่จะใช้ช่วงที่นั่น
mikeserv

4

นี่เป็นแนวทางบางประการ:

  • GNU grepและtr: ค้นหาคำทั้งหมดและทำให้เป็นตัวพิมพ์เล็ก

    grep -Po '\w+' file | tr '[A-Z]' '[a-z]'
  • GNU grep และ perl: เหมือนด้านบน แต่ perl จะจัดการการแปลงเป็นตัวพิมพ์เล็ก

    grep -Po '\w+' file | perl -lne 'print lc()'
  • perl: ค้นหาตัวอักษรทั้งหมดและพิมพ์เป็นตัวพิมพ์เล็ก (ขอบคุณ @steeldriver):

    perl -lne 'print lc for /[a-z]+/ig' file
  • sed: ลบอักขระทั้งหมดที่ไม่ใช่ตัวอักษรหรือช่องว่างแทนที่อักขระตัวอักษรทั้งหมดด้วยตัวพิมพ์เล็กและแทนที่ช่องว่างทั้งหมดด้วยบรรทัดใหม่ โปรดทราบว่านี่ถือว่าช่องว่างทั้งหมดเป็นช่องว่างไม่มีแท็บ

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

2
สิ่งที่ต้องการperl -lne 'print lc for /[[:alpha:]]+/g'ยังทำงานได้หรือไม่ หรือมันเป็นสไตล์ที่ไม่ดี? (ฉันใหม่เพื่อ perl และพยายามที่จะเรียนรู้!)
steeldriver

@steeldriver ใช่มันเป็นหนึ่งที่ดี! หากคุณกำลังเรียนรู้ Perl ฉันแน่ใจว่าคุณได้พบคำขวัญ: TMTOWTDI :) ขอบคุณฉันจะเพิ่มอันนั้น
terdon

3
กับรุ่นใหม่ (> 4.2.1)sed -z 's/\W*\(\w\+\)\W*/\L\1\n/g'
Costas

@ Costas ah sedทำได้\wแล้วหรือยัง เย็น!
terdon

@terdon - มันทำมานานแล้ว แต่เนื่องจาก Costas ไม่ได้พูดถึงมันฉันคิดว่าสิ่งที่น่าสนใจที่สุดเกี่ยวกับความคิดเห็นข้างต้นคือสวิตช์ ero delimit sedของGNU -z- มันหมุนรอบ\0NULs มากกว่าการขึ้นบรรทัดใหม่ ค่อนข้างเท่ห์เมื่อคุณทำอะไรที่ชอบtar -c . | tr -s \\0 | sed -z ...- แต่ค่อนข้างช้า
mikeserv

4

ใช่. คุณสามารถทำ w / trในสถาน ASCII (ซึ่งเป็นสำหรับ GNUtrแล้วชนิดของขอบเขตเท่านั้น) คุณสามารถใช้คลาส POSIX หรือคุณสามารถอ้างอิงค่าไบต์ของอักขระแต่ละตัวด้วยหมายเลขฐานแปด คุณสามารถแยกการแปลงข้ามช่วงได้เช่นกัน

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

คำสั่งดังกล่าวจะแปลงอักขระตัวพิมพ์ใหญ่ทั้งหมดให้เป็นตัวพิมพ์เล็กละเว้นอักขระตัวพิมพ์เล็กทั้งหมดและแปลงอักขระอื่นทั้งหมดเป็นบรรทัดใหม่ แน่นอนว่าคุณต้องจบด้วยบรรทัดว่างเปล่าจำนวนหนึ่ง tr -sซ้ำ queeze สลับอาจเป็นประโยชน์ในกรณีที่ว่า แต่ถ้าคุณใช้มันควบคู่ไปกับ[:upper:]การ[:lower:]เปลี่ยนแปลงแล้วคุณลมขึ้นบีบตัวพิมพ์ใหญ่ตัวละครได้เป็นอย่างดี ด้วยวิธีนี้มันยังต้องใช้ตัวกรองที่สองเช่น ...

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

...หรือ...

LC... tr ... | grep .

... และดังนั้นมันจึงสะดวกสบายกว่าการทำ ...

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

... ซึ่งบีบการ-cใช้อักขระตัวอักษรตามลำดับในการขึ้นบรรทัดใหม่หนึ่งชิ้นจากนั้นทำการแปลงด้านบนและล่างในอีกด้านหนึ่งของท่อ

ไม่ได้หมายความว่าช่วงของลักษณะนั้นจะไม่มีประโยชน์ สิ่งที่ชอบ:

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

... สามารถทำได้ค่อนข้างดีเพราะมันแปลงไบต์อินพุตเป็นตัวเลขทั้งหมดผ่านสเปกตรัมการแพร่กระจายของค่าของพวกเขา ไม่ต้องเสียไม่อยากรู้

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

เนื่องจากddสามารถทำทั้งสองอย่างunblockและlcaseแปลงในเวลาเดียวกันมันอาจเป็นไปได้ที่จะผ่านงานส่วนใหญ่ออกไป แต่นั่นจะมีประโยชน์จริง ๆ ถ้าคุณสามารถทำนายจำนวนไบต์ต่อคำได้อย่างถูกต้องหรืออย่างน้อยสามารถคั่นแต่ละคำด้วยช่องว่างไว้ล่วงหน้าเพื่อนับจำนวนไบต์ที่คาดเดาได้เพราะunblockกินช่องว่างท้ายท้ายแต่ละบล็อก


+2 คะแนนโบนัสสำหรับการddมีส่วนร่วม :)
tlehman

@TobiLehman - ฉันยินดีมากที่คุณอนุมัติ
mikeserv
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.