Xóa tất cả các ký tự không phải mã ascii khỏi luồng công việc (tệp)


12

Làm cách nào để xóa tất cả các ký tự không phải mã ascii khỏi một tệp? Sẽ có một lệnh cụ thể để thực hiện điều này?

grep --colour='auto' -P -n'[^\x00-\x7]' /usr/local/...

Tôi tin rằng điều này tìm thấy các ký tự trong quy trình làm việc, nhưng làm cách nào để loại bỏ tất cả các phiên bản của các ký tự được đề cập?



2
có liên quan: nếu bạn chỉ muốn tránh các vấn đề với ký tự điều khiển (thay vì âm thầm loại bỏ chúng), bạn chỉ cần sử dụng cat -vđể hiển thị chúng trong phản hồi ASCII cho chúng. (ví dụ: ^Gcho \007)
Matija Nalis

1
Khi bạn nói "các ký tự không phải ascii", bạn cũng bao gồm các ký tự có dấu?
Thuyền trưởng Man

1
@MatijaNalis Thông tin thêm về đại diện: en.wikipedia.org/wiki/Caret_notation
wjandrea

1
Trường hợp sử dụng là gì? Rất thường có các công cụ cụ thể hoặc các cách tiếp cận khác nhau hoạt động tốt hơn nhiều so với việc loại bỏ một loạt các ký tự đặc biệt. Xin lưu ý rằng ASCII không bao gồm một số ký tự "đặc biệt" như tab dọc, chuông và NUL - bạn có chắc bạn không có nghĩa là các ký tự có thể in được không?
l0b0

Câu trả lời:


25

Các ký tự ASCII là các ký tự trong phạm vi từ 0 đến 177 (bát phân) .

Để xóa các ký tự bên ngoài phạm vi này trong một tệp, sử dụng

LC_ALL=C tr -dc '\0-\177' <file >newfile

Các trlệnh là một tiện ích hoạt động trên ký tự đơn , hoặc thay thế chúng với các nhân vật khác duy nhất (phiên âm), xóa chúng, hoặc nén chạy của nhân vật tương tự thành một nhân vật duy nhất.

Lệnh trên sẽ đọc từ filevà ghi nội dung đã sửa đổi vào newfile. Các -dtùy chọn để trlàm cho tiện ích ký tự xóa (thay vì chuyển ngữ chúng), và -clàm cho nó xem xét nhân vật bên ngoài khoảng thời gian nhất định (thay vì bên trong).

LC_ALL=Cđảm bảo rằng mỗi giá trị byte tạo thành một ký tự hợp lệ. Nếu không có nó, một số trtriển khai sẽ hủy bỏ nếu họ tìm thấy chuỗi byte không tạo thành các ký tự hợp lệ trong mã hóa ký tự của miền địa phương.


Để thay thế tệp gốc bằng tệp đã sửa đổi, hãy sử dụng

LC_ALL=C tr -dc '\0-\177' <file >newfile &&
mv newfile file

Điều này đổi tên tệp mới thành tên của tệp cũ sau khi trđã hoàn thành thành công. Nếu trkhông hoàn thành thành công, vì nó không thể đọc tệp gốc hoặc không ghi vào tệp mới, tệp gốc sẽ được giữ nguyên.

Ngoài ra, để bảo tồn càng nhiều càng tốt dữ liệu meta (quyền, v.v.) của tệp gốc, hãy sử dụng

cp file tmpfile &&
LC_ALL=C tr -dc '\0-\177' <tmpfile >file &&
rm tmpfile


9

Nếu tất cả những gì bạn cần là một biểu thức chính quy: [\x00-\x7F]bạn có thể áp dụng cho một số tiện ích:

<file LC_ALL=C   sed   's/[^\o0-\o177]//g'      # GNU sed without POSIXLY_CORRECT
<file LC_ALL=C   awk   '{gsub(/[^\0-\177]/,"");print}'
<file            perl  -pe 's/[^[:ascii:]]//g;'
<file LC_ALL=C   tr    -dc '\0-\177'

Hiểu rằng sed, awk và perl mong đợi "tệp văn bản" như được định nghĩa trong Unix. Tất cả đều hoạt động tốt trong trường hợp này. Nhưng cụ thể, awk thêm một dòng mới (dù nó có tồn tại trong tệp nguồn hay không) (sử dụng printf sẽ loại bỏ TẤT CẢ các dòng mới trên đầu vào). Tr được thiết kế để làm việc với bất kỳ loại tập tin. Tuy nhiên, NUL ( \0) không phải là ký tự hợp lệ trong tệp văn bản POSIX và nên tránh:

Các dòng không chứa ký tự NUL ...

Trong thực tế, nhiều nhân vật điều khiển sẽ tạo ra các vấn đề khác trong một số điều kiện cụ thể.
Vì vậy, có lẽ bạn cần[\x07-\x0d\x20-\x7e]

<file LC_ALL=C   sed   's/[^\o007-\o015\o040-\o176]//g'            # GNU sed without POSIXLY_CORRECT
<file LC_ALL=C   awk   '{gsub(/[^\0-\15\40-\176]/,"");print}'
<file            perl  -pe 's/[^\x{7}-\x{d}\x{20}-\x{7e}]//g;'
<file LC_ALL=C   tr    -dc '\7-\15\40-\176'

Phạm vi 7-13 (theo số thập phân) là \a\b\t\n\v\f\r(theo thứ tự).
Một phạm vi tương tự (có thể di động hơn) có thể được viết là [^[:space:][:print:]] (similar because it doesn't include\ a \ b` - chuông và backspace--).

<file LC_ALL=C   sed   's/[^[:space:][:print:]]//g'  # GNU sed without POSIXLY_CORRECT
<file LC_ALL=C   awk   '{gsub(/[^[:space:][:print:]]/,"");print}'
<file            perl   -pe 's/[^[:space:][:print:]]//g;'
<file LC_ALL=C   tr     -dc '[:space:][:print:]'

Liên quan:
Regex bất kỳ ký tự ASCII
giải pháp Perl
Tệp văn bản Posix


Lưu ý rằng đầu vào trcó thể là bất kỳ loại tệp nào, không chỉ các tệp văn bản. awkmặt khác, có một tập tin văn bản.
Kusalananda

Tôi khá khó khăn để tìm bất cứ điều gì khác để gọi một tệp "chỉ các ký tự ascii" bất cứ thứ gì ngoại trừ "tệp văn bản" (vâng, vâng: theo thuật ngữ cư sĩ). @Kusalananda (lưu ý về awk được thêm vào dù sao).
Isaac

Lưu ý rằng đó gensub()là một phần mở rộng gawk. Bạn muốn gsub(...); printvà sử dụng bát phân thay vì các chuỗi hex (và LC_ALL = C) để có thể (hơn) di động.
Stéphane Chazelas

@ StéphaneChazelas Hạn chế của GNU sed làm cho cú pháp GNU cụ thể (Tôi hiểu vấn đề POSIXLY_CORRECT).
Isaac

[^\o0]là để khớp với các ký tự khác với dấu gạch chéo ngược, o và 0 trong POSIX sed(trong tất cả các triển khai trừ GNU sed). Đó không phải là giới hạn của GNU sedmà là phần mở rộng không tuân thủ, đó là lý do tại sao nó bị vô hiệu hóa khi POSIXLY_CORRECT ở trong môi trường).
Stéphane Chazelas
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.