Ersetzen Sie nicht druckbare Zeichen in Perl und Sed

10

Ich muss einige nicht druckbare Zeichen durch Leerzeichen in der Datei ersetzen.

Genauer gesagt, werden alle Zeichen von 0x00bis zu 0x1F, mit der Ausnahme 0x09(TAB), 0x0A(neue Zeile), 0x0D(CR)

Bis jetzt musste ich nur den 0x00Charakter ersetzen . Da mein vorheriges Betriebssystem AIX war (ohne GNU-Befehle), kann ich es nicht verwenden sed(nun, ich kann, aber es hatte einige Einschränkungen). Also fand ich den nächsten Befehl mit perl, der wie erwartet funktionierte:

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

Jetzt arbeite ich unter Linux und habe erwartet, dass ich den sedBefehl verwenden kann.

Meine Fragen:

  • Ist dieser Befehl geeignet, um diese Zeichen zu ersetzen? Ich habe es versucht und es scheint zu funktionieren, aber ich möchte sichergehen:

    perl -p -e 's/[\x00-\x08\x0B\x0C\x0E-\x1F]/ /g' $FILE_IN > $FILE_OUT  
  • Ich dachte perl -pfunktioniert wie sed. Warum funktioniert der vorherige Befehl (zumindest schlägt er nicht fehl) und der nächste nicht?

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

    Es sagt mir:

    sed: -e Ausdruck # 1, char 34: Ungültiges Kollatierungszeichen

Albert
quelle
perl -pdruckt das Endprodukt aus, stdinnachdem Sie die gewünschten Vorgänge ausgeführt haben. In diesem Fall handelt es sich nur um einen Ersatz. sedDer reguläre Ausdruck könnte anders sein als perl.
SDKKS

Antworten:

11

Das ist ein typischer Job für tr:

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

In Ihrem Fall funktioniert dies nicht, sedda Sie sich in einem Gebietsschema befinden, in dem diese Bereiche keinen Sinn ergeben. Wenn Sie die Arbeit mit Byte - Werten wollen wie auf Zeichen entgegengesetzt und in dem die Reihenfolge auf dem numerischen Wert dieses Bytes basiert, ist Ihre beste Wette ist , um das C - Gebietsschema zu verwenden . Ihr Code hätte LC_ALL=Cmit GNU funktioniert sed, aber die Verwendung sed(geschweige denn perl) ist hier etwas übertrieben (und diese \xXXsind nicht sedimplementierungsübergreifend portierbar , während dieser trAnsatz POSIX ist).

Sie können auch der Vorstellung Ihres Gebietsschemas vertrauen, welche druckbaren Zeichen enthalten sind:

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

Mit GNU tr(wie es normalerweise auf Linux-basierten Systemen zu finden ist) funktioniert dies jedoch nur in Gebietsschemas, in denen Zeichen Einzelbyte sind (also normalerweise nicht UTF-8).

Im C-Gebietsschema würde dies auch DEL (0x7f) und alle obigen Bytewerte ausschließen (nicht in ASCII).

In UTF-8-Gebietsschemas können Sie GNU verwenden, seddas nicht das Problem trhat, das GNU hat:

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

(beachten Sie, dass diejenigen \r, \tsind nicht Standard, und GNU sedwird sie nicht erkennen , wenn POSIXLY_CORRECTin der Umgebung ist (wird sie als Backslash behandeln, r und t Teil des Satzes ist , wie POSIX erfordert)).

Es werden jedoch keine Bytes konvertiert, die keine gültigen Zeichen bilden.

Stéphane Chazelas
quelle
Ich verstehe, was trBefehl tut. Ich verstehe (mehr oder weniger) was LC_ALL = Cist, aber nicht alle zusammen. tr -dEntfernt trotzdem diese Zeichen, aber ich möchte durch Leerzeichen ersetzen. Entschuldigung, der Titel war falsch. Ich habe gerade gemerkt, als @don_crissti modifiziert wurde.
Albert
@ Albert, sorry. Siehe die Bearbeitung und den Link, den ich hinzugefügt habe.
Stéphane Chazelas
Ich bin mir bei der Codierung nicht sicher. Diese Datei stammt aus einer HOST-Umgebung, die EBCDIC-Codierung verwendet, und wird mithilfe von an Linux übertragen XCOM. Zum Beispiel werden Nicht-ASCII-Zeichen wie Ékodifiziert (mit od -xa) als 0xC9, also denke ich, wäre es ISO-8859-1.
Albert
@ Albert, wahrscheinlich. Sie können verwenden locale -a, um festzustellen, ob auf Ihrem System Gebietsschemas mit iso8859-1 als Zeichensatz vorhanden sind, und LC_CTYPE=<that-locale> tr ...[:print:]...um nicht druckbare Dateien in diesem Gebietsschema zu konvertieren. Oder Sie können iconv verwenden, um diese Dateien in den Zeichensatz Ihres Gebietsschemas zu konvertieren.
Stéphane Chazelas
Ich denke, es wird nicht benötigt, da der Zeichensatz meines Gebietsschemas auf eingestellt ist LC_ALL=en_US.iso88591. Ihr Befehl ( tr -c '[:print:]\t\r\n' '[ *]') funktioniert also einwandfrei, ohne das Gebietsschema zu ändern oder die Datei zu konvertieren. Vielen Dank.
Albert
0

Ich habe versucht, eine Benachrichtigung über libnotify mit Inhalten zu senden, die möglicherweise nicht druckbare Zeichen enthalten. Die vorhandenen Lösungen haben bei mir nicht ganz funktioniert (Verwenden einer Whitelist von Zeichen mit trWerken, Entfernen aller Multi-Byte-Zeichen).

Folgendes hat beim Bestehen des 💩-Tests funktioniert:

message=$(iconv --from-code=UTF-8 -c <<< "$message")
Wir sind alle Monica
quelle