Các lệnh tr có thể được xâu chuỗi để tránh nhiều quá trình tr trong một đường ống không?


11

Tôi có một loạt các txt file, tôi muốn xuất chúng hạ-cased, chỉ có chữ cái và một từ mỗi dòng, tôi có thể làm điều đó với một số trlệnh trong một đường ống như thế này:

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

Có thể làm điều này trong một lần quét? Tôi có thể viết một chương trình C để làm điều này, nhưng tôi cảm thấy như có một cách để làm điều đó bằng tr, sed, awkhoặc perl.


Bạn đang sử dụng hệ điều hành nào? Bạn có quyền truy cập vào các công cụ GNU không?
terdon

Câu trả lời:


9

Bạn có thể kết hợp nhiều bản dịch (ngoại trừ các trường hợp phức tạp liên quan đến các bộ phụ thuộc địa phương chồng chéo), nhưng bạn không thể kết hợp xóa với bản dịch.

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

Hai cuộc gọi trcó khả năng nhanh hơn một cuộc gọi đến các công cụ phức tạp hơn, nhưng điều này phụ thuộc rất nhiều vào kích thước đầu vào, vào tỷ lệ của các ký tự khác nhau, vào việc triển khai trvà các công cụ cạnh tranh, trên hệ điều hành, trên số lõi, v.v.


Tôi không chắc chắn kết hợp lạitr -s '[:upper:] [:punct:]' '[:lower:]\n' <doyle_sherlock_holmes.txt
Costas

1
@Costas Điều đó sẽ chuyển đổi dấu câu thành dòng mới. Ứng dụng cụ thể này có thể ổn, nhưng đầu ra không giống với bản gốc.
Gilles 'SO- ngừng trở nên xấu xa'

@Costas - trong khi điều mới có thể được tích lũy ở đây, tôi không nghĩ việc ép các ký tự viết hoa sẽ là. Ví dụ: printf 'A.AAAA,A' | tr -s '[:upper:] [:punct:]' '[:lower:][\n*]'được a\na\na'và việc chuyển đổi ... '[:lower:]\n'có thể không nhất thiết phải làm bất cứ điều gì cả '[:punct:]'- dù sao đi nữa, một số trs sẽ cắt ngắn set1 để khớp với 2 và một số sẽ thực hiện ngụ ý [\n*]. Tốt hơn là chỉ sử dụng phạm vi đó.
mikeerv

4

Dưới đây là một vài cách tiếp cận:

  • GNU greptr: tìm tất cả các từ và viết chúng thành chữ thường

    grep -Po '\w+' file | tr '[A-Z]' '[a-z]'
  • GNU grep và perl: như trên nhưng perl xử lý việc chuyển đổi thành chữ thường

    grep -Po '\w+' file | perl -lne 'print lc()'
  • perl: tìm tất cả các ký tự chữ cái và in chúng bằng chữ thường (cảm ơn @steel ấn):

    perl -lne 'print lc for /[a-z]+/ig' file
  • sed: xóa tất cả các ký tự không phải là chữ cái hoặc dấu cách, thay thế tất cả các ký tự chữ cái bằng các phiên bản chữ thường và thay thế tất cả các khoảng trắng bằng dòng mới. Lưu ý rằng điều này giả định rằng tất cả các khoảng trắng là khoảng trắng, không có tab.

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

2
Một cái gì đó như perl -lne 'print lc for /[[:alpha:]]+/g'cũng sẽ làm việc? hay là phong cách kém? (Tôi mới biết về perl và đang cố gắng học hỏi!)
Steeldo

@steel ấn có nó sẽ tốt, một trong những tốt đẹp! Nếu bạn đang học Perl, tôi chắc chắn bạn đã bắt gặp phương châm của nó: TMTOWTDI :) Cảm ơn, tôi sẽ thêm cái đó.
terdon

3
Với phiên bản mới (> 4.2.1)sed -z 's/\W*\(\w\+\)\W*/\L\1\n/g'
Costas

@Costas ah, sedcó thể làm gì \wbây giờ? Mát mẻ!
terdon

@terdon - nó làm điều đó cho một lúc, nhưng vì Costas không đề cập đến nó, tôi nghĩ rằng điều thú vị nhất về những nhận xét trên là GNU sed's -zero phân định chuyển đổi - nó chu kỳ hơn \0NULlà hơn là dòng mới. Khá tuyệt khi bạn làm một cái gì đó như tar -c . | tr -s \\0 | sed -z ...- nhưng hơi chậm.
mikeerv

4

Đúng. Bạn có thể làm điều đó với trngôn ngữ ASCII ( trdù sao, đối với GNU , đây là loại nội dung duy nhất của nó) . Bạn có thể sử dụng các lớp POSIX hoặc bạn có thể tham chiếu các giá trị byte của mỗi ký tự theo số bát phân. Bạn cũng có thể phân chia các biến đổi của chúng trên các phạm vi.

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

Lệnh trên sẽ chuyển đổi tất cả các ký tự chữ hoa thành chữ thường, bỏ qua toàn bộ ký tự chữ thường và chuyển đổi tất cả các ký tự khác thành dòng mới. Tất nhiên, sau đó bạn kết thúc với một tấn các dòng trống. Các tr -slặp queeze chuyển có thể hữu ích trong trường hợp đó, nhưng nếu bạn sử dụng nó bên cạnh [:upper:]để [:lower:]chuyển đổi sau đó bạn gió lên ép ký tự chữ hoa là tốt. Theo cách đó, nó vẫn yêu cầu bộ lọc thứ hai như ...

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

...hoặc là...

LC... tr ... | grep .

... và do đó, nó trở nên ít thuận tiện hơn nhiều so với làm ...

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

... Việc nén các -cký tự chữ cái theo trình tự thành một dòng mới một mảnh, sau đó thực hiện chuyển đổi từ trên xuống dưới ở phía bên kia của đường ống.

Điều đó không có nghĩa là phạm vi của bản chất đó không hữu ích. Những thứ như:

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

... có thể khá tiện dụng vì nó chuyển đổi các byte đầu vào thành tất cả các chữ số trên một phổ trải rộng các giá trị của chúng. Lãng phí không, muốn không, bạn biết.

Một cách khác để làm biến đổi có thể liên quan 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

Bởi vì ddcó thể thực hiện cả hai unblocklcasechuyển đổi cùng một lúc, thậm chí có thể chuyển phần lớn công việc cho nó. Nhưng điều đó chỉ thực sự hữu ích nếu bạn có thể dự đoán chính xác số byte trên mỗi từ - hoặc ít nhất có thể đệm từng từ với khoảng trắng trước một số byte có thể dự đoán được, bởi vì unblockăn các khoảng trắng ở cuối mỗi khối.


+2 điểm thưởng khi ddtham gia :)
tlehman 11/2/2015

@TobiLehman - Tôi rất vui khi bạn chấp thuận.
mikeerv
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.