Thay thế các ký tự không in được bằng perl và sed


11

Tôi cần thay thế một số ký tự không in được bằng dấu cách trong tệp.

Cụ thể, tất cả các ký tự từ 0x00tối đa 0x1F, ngoại trừ 0x09(TAB), 0x0A(dòng mới), 0x0D(CR)

Cho đến bây giờ, tôi chỉ cần thay thế 0x00nhân vật. Vì hệ điều hành trước của tôi là AIX (không có lệnh GNU), tôi không thể sử dụng sed(tốt, tôi có thể nhưng nó có một số hạn chế). Vì vậy, tôi tìm thấy lệnh tiếp theo bằng cách sử dụng perl, hoạt động như mong đợi:

perl -p -e 's/\x0/ /g' $FILE_IN > $FILE_OUT 

Bây giờ tôi đang làm việc trên Linux, vì vậy tôi mong đợi có thể sử dụng sedlệnh.

Những câu hỏi của tôi:

  • Là lệnh này thích hợp để thay thế những nhân vật? Tôi đã thử, và nó dường như hoạt động, nhưng tôi muốn chắc chắn:

    perl -p -e 's/[\x00-\x08\x0B\x0C\x0E-\x1F]/ /g' $FILE_IN > $FILE_OUT  
  • Tôi nghĩ rằng perl -plàm việc như sed. Vậy, tại sao lệnh trước hoạt động (ít nhất, nó không thất bại), và lệnh tiếp theo thì không?

    sed -e 's/[\x00-\x08\x0B\x0C\x0E-\x1F]/ /g' $FILE_IN > $FILE_OUT   

    Nó nói với tôi:

    sed: -e biểu thức # 1, char 34: Ký tự đối chiếu không hợp lệ


perl -pin sản phẩm cuối cùng stdinsau khi thực hiện các thao tác bạn mong muốn, trong trường hợp này chỉ là thay thế. sedRegex của có thể khác hơn perl.
sdkks

Câu trả lời:


11

Đó là một công việc điển hình cho tr:

LC_ALL=C tr '\0-\10\13\14\16-\37' '[ *]' < in > out

Trong trường hợp của bạn, nó không hoạt động sedvì bạn đang ở một địa phương nơi những phạm vi đó không có ý nghĩa. Nếu bạn muốn làm việc với các giá trị byte như trái ngược với nhân vật và nơi trật tự được dựa trên giá trị số của những byte, đặt cược tốt nhất của bạn là sử dụng các locale C . Mã của bạn sẽ hoạt động với LC_ALL=CGNU sed, nhưng sử dụng sed(huống chi perl) là hơi quá mức ở đây (và những thứ đó \xXXkhông thể di chuyển qua các sedtriển khai trong khi trphương pháp này là POSIX).

Bạn cũng có thể tin tưởng ý tưởng địa phương của bạn về các ký tự có thể in được với:

tr -c '[:print:]\t\r\n' '[ *]'

Nhưng với GNU tr(như thường thấy trên các hệ thống dựa trên Linux), nó chỉ hoạt động ở các địa phương nơi các ký tự là byte đơn (thông thường, không phải UTF-8).

Trong miền địa phương C, điều đó cũng sẽ loại trừ DEL (0x7f) và tất cả các giá trị byte ở trên (không phải trong ASCII).

Trong các ngôn ngữ UTF-8, bạn có thể sử dụng GNU sedmà không có vấn đề GNU trcó:

sed 's/[^[:print:]\r\t]/ /g' < in > out

(lưu ý rằng những thứ đó \r, \tkhông phải là tiêu chuẩn và GNU sedsẽ không nhận ra chúng nếu POSIXLY_CORRECTở trong môi trường (sẽ coi chúng là dấu gạch chéo ngược, r và t là một phần của tập hợp như POSIX yêu cầu)).

Nó sẽ không chuyển đổi các byte không tạo thành các ký tự hợp lệ nếu có.


Tôi hiểu những gì trlệnh làm. Tôi hiểu (ít nhiều) những gì LC_ALL = Clà, nhưng không phải tất cả cùng nhau. Tuy nhiên, tr -dloại bỏ các ký tự đó, nhưng tôi muốn thay thế bằng khoảng trắng. Xin lỗi, tiêu đề đã sai. Tôi mới nhận ra, khi @don_crissti sửa đổi.
Albert

@ Albert, xin lỗi. Xem chỉnh sửa và liên kết tôi đã thêm.
Stéphane Chazelas

Tôi không chắc chắn về mã hóa. Tệp đó xuất phát từ môi trường HOST, sử dụng mã hóa EBCDIC và được chuyển sang Linux bằng cách sử dụng XCOM. Ví dụ, các ký tự không phải ASCII như Éđược mã hóa (sử dụng od -xa) như 0xC9, vì vậy tôi đoán nó sẽ như vậy ISO-8859-1.
Albert

@ Albert, có lẽ. Bạn có thể sử dụng locale -ađể xem nếu có các địa điểm có iso8859-1 làm bộ ký tự trên hệ thống của bạn và sử dụng LC_CTYPE=<that-locale> tr ...[:print:]...để chuyển đổi các bản in không in được trong miền địa phương đó. Hoặc bạn có thể sử dụng iconv để chuyển đổi các tệp đó sang bộ ký tự địa phương.
Stéphane Chazelas

Tôi nghĩ rằng nó không cần thiết, bởi vì bộ ký tự địa phương của tôi được đặt thành LC_ALL=en_US.iso88591. Vì vậy, lệnh của bạn ( tr -c '[:print:]\t\r\n' '[ *]') hoạt động hoàn hảo mà không cần sửa đổi ngôn ngữ hoặc chuyển đổi tệp. Cảm ơn rất nhiều.
Albert

0

Tôi đã cố gắng gửi thông báo qua libnotify, với nội dung có thể chứa các ký tự không thể in được. Các giải pháp hiện tại không hoàn toàn phù hợp với tôi (sử dụng danh sách trắng các ký tự sử dụng trcác tác phẩm, nhưng loại bỏ bất kỳ ký tự nhiều byte nào).

Đây là những gì đã làm việc, trong khi vượt qua bài kiểm tra::

message=$(iconv --from-code=UTF-8 -c <<< "$message")
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.