Ganti karakter yang tidak dapat dicetak dalam perl dan sed

11

Saya perlu mengganti beberapa karakter yang tidak dapat dicetak dengan spasi dalam file.

Secara khusus, semua karakter dari 0x00hingga 0x1F, kecuali 0x09(TAB), 0x0A(baris baru), 0x0D(CR)

Sampai sekarang, saya hanya perlu mengganti 0x00karakter. Karena OS saya sebelumnya adalah AIX (tanpa perintah GNU), saya tidak dapat menggunakan sed(well, saya bisa tetapi memiliki beberapa keterbatasan). Jadi, saya menemukan perintah berikutnya menggunakan perl, yang berfungsi seperti yang diharapkan:

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

Sekarang saya sedang bekerja di Linux, jadi saya diharapkan dapat menggunakan sedperintah.

Pertanyaan saya:

  • Apakah perintah ini sesuai untuk mengganti karakter-karakter itu? Saya mencoba, dan tampaknya berhasil, tetapi saya ingin memastikan:

    perl -p -e 's/[\x00-\x08\x0B\x0C\x0E-\x1F]/ /g' $FILE_IN > $FILE_OUT  
  • Saya pikir perl -pberfungsi sebagai sed. Jadi, mengapa perintah sebelumnya berfungsi (setidaknya, itu tidak gagal), dan yang berikutnya tidak?

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

    Ini memberitahu saya:

    sed: -e ekspresi # 1, char 34: Karakter susunan tidak valid

Albert
sumber
perl -pmencetak produk akhir stdinsetelah melakukan operasi yang Anda inginkan, dalam hal ini hanya penggantian. sedRegex mungkin berbeda dari perl.
sdkks

Jawaban:

11

Itu pekerjaan yang khas untuk tr:

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

Dalam kasus Anda, ini tidak berfungsi sedkarena Anda berada di lokal di mana rentang tersebut tidak masuk akal. Jika Anda ingin bekerja dengan nilai byte sebagai lawan dari karakter dan di mana urutannya didasarkan pada nilai numerik dari byte tersebut, taruhan terbaik Anda adalah menggunakan C locale . Kode Anda akan bekerja dengan LC_ALL=CGNU sed, tetapi menggunakan sed(apalagi perl) agak berlebihan di sini (dan itu \xXXtidak portabel di seluruh sedimplementasi sementara trpendekatan ini POSIX).

Anda juga dapat mempercayai ide lokal Anda tentang apa saja karakter yang dapat dicetak dengan:

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

Tetapi dengan GNU tr(seperti yang biasanya ditemukan pada sistem berbasis Linux), yang hanya berfungsi di lokal di mana karakter adalah byte tunggal (jadi biasanya, bukan UTF-8).

Di lokal C, itu juga mengecualikan DEL (0x7f) dan semua nilai byte di atas (tidak dalam ASCII).

Di lokal UTF-8, Anda bisa menggunakan GNU sedyang tidak memiliki masalah yang trdimiliki GNU :

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

(catat bahwa itu \r, \tbukan standar, dan GNU sedtidak akan mengenalinya jika POSIXLY_CORRECTada di lingkungan (akan memperlakukan mereka sebagai backslash, r dan t menjadi bagian dari set seperti yang POSIX minta)).

Itu tidak akan mengkonversi byte yang tidak membentuk karakter yang valid jika ada.

Stéphane Chazelas
sumber
Saya mengerti apa yang trdilakukan perintah. Saya mengerti (kurang lebih) apa LC_ALL = Citu, tetapi tidak semuanya. Meskipun demikian tr -dmenghapus karakter tersebut, tetapi saya ingin mengganti dengan spasi. Maaf, judul salah. Saya baru sadar, ketika @don_crissti dimodifikasi.
Albert
@Albert, maaf. Lihat hasil edit dan tautan yang saya tambahkan.
Stéphane Chazelas
Saya tidak yakin tentang penyandian. File itu berasal dari lingkungan HOST, yang menggunakan pengkodean EBCDIC, dan ditransfer ke Linux menggunakan XCOM. Sebagai contoh, non-ASCII-karakter seperti Édikodifikasikan (menggunakan od -xa) sebagai 0xC9, jadi saya kira itu akan menjadi ISO-8859-1.
Albert
@Albert, mungkin. Anda dapat menggunakan locale -auntuk melihat apakah ada lokal dengan iso8859-1 sebagai charset di sistem Anda dan gunakan LC_CTYPE=<that-locale> tr ...[:print:]...untuk mengonversi non-printable di lokal itu. Atau Anda dapat menggunakan iconv untuk mengonversi file-file itu ke rangkaian karakter lokal Anda.
Stéphane Chazelas
Saya pikir itu tidak diperlukan, karena charset lokal saya diatur ke LC_ALL=en_US.iso88591. Jadi, perintah Anda ( tr -c '[:print:]\t\r\n' '[ *]') berfungsi sempurna tanpa mengubah lokal atau mengonversi file. Terima kasih banyak.
Albert
0

Saya mencoba mengirim pemberitahuan melalui libnotify, dengan konten yang mungkin mengandung karakter yang tidak dapat dicetak. Solusi yang ada tidak cukup berfungsi untuk saya (menggunakan daftar putih karakter menggunakan trkarya, tetapi menghapus semua karakter multi-byte).

Inilah yang berhasil, saat melewati tes 💩:

message=$(iconv --from-code=UTF-8 -c <<< "$message")
Kita Semua Monica
sumber