Sostituisci i caratteri non stampabili in perl e sed


11

Devo sostituire alcuni caratteri non stampabili con spazi nel file.

In particolare, tutti i caratteri 0x00fino a 0x1F, tranne 0x09(TAB), 0x0A(nuova riga), 0x0D(CR)

Fino ad ora, dovevo solo sostituire il 0x00personaggio. Dato che il mio sistema operativo precedente era AIX (senza comandi GNU), non posso usarlo sed(bene, ma ho avuto alcune limitazioni). Quindi, ho trovato il prossimo comando usando perl, che ha funzionato come previsto:

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

Ora sto lavorando su Linux, quindi mi aspettavo di poter usare il sedcomando.

Le mie domande:

  • Questo comando è appropriato per sostituire quei caratteri? Ho provato, e sembra funzionare, ma voglio essere sicuro:

    perl -p -e 's/[\x00-\x08\x0B\x0C\x0E-\x1F]/ /g' $FILE_IN > $FILE_OUT  
  • Ho pensato che perl -pfunziona come sed. Quindi, perché il comando precedente funziona (almeno, non fallisce) e il successivo no?

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

    Mi dice:

    sed: -e espressione # 1, carattere 34: carattere di confronto non valido


perl -pstampa il prodotto finale stdindopo aver eseguito le operazioni desiderate, in questo caso è solo una sostituzione. sedIl regex potrebbe essere diverso da perl.
sdkks,

Risposte:


11

Questo è un lavoro tipico per tr:

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

Nel tuo caso, non funziona sedperché ti trovi in ​​una locale in cui tali intervalli non hanno senso. Se si vuole lavorare con i valori di byte al contrario di personaggi e in cui l'ordine si basa sul valore numerico di questi byte, la soluzione migliore è quella di utilizzare la versione locale C . Il tuo codice avrebbe funzionato con LC_ALL=CGNU sed, ma usare sed(figuriamoci perl) è un po 'eccessivo qui (e quelli \xXXnon sono portatili tra le sedimplementazioni mentre questo trapproccio è POSIX).

Puoi anche fidarti dell'idea del tuo locale di cosa sono i caratteri stampabili con:

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

Ma con GNU tr(come in genere si trova sui sistemi basati su Linux), funziona solo in locali in cui i caratteri sono a byte singolo (quindi in genere, non UTF-8).

Nella locale C, ciò escluderebbe anche DEL (0x7f) e tutti i valori di byte sopra (non in ASCII).

Nelle versioni locali UTF-8, è possibile utilizzare GNU sedche non presenta il problema che GNU trpresenta:

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

(nota che coloro \r, \tnon sono standard, e GNU sednon riconoscerà loro se POSIXLY_CORRECTè nell'ambiente (li trattano come barra rovesciata, ret essendo parte del set come richiede POSIX)).

Non convertirebbe byte che non formano caratteri validi, comunque.


Capisco che trcomando fa. Ho capito (più o meno) quello che LC_ALL = Cè, ma non tutti insieme. Tuttavia tr -drimuove quei personaggi, ma voglio sostituirli con spazi. Spiacente, il titolo era sbagliato. Ho appena realizzato, quando @don_crissti ha modificato.
Albert,

@Albert, scusa. Vedi la modifica e il link che ho aggiunto.
Stéphane Chazelas,

Non sono sicuro della codifica. Tale file proviene da un ambiente HOST, che utilizza la codifica EBCDIC, e viene trasferito su Linux mediante XCOM. Ad esempio, caratteri non ASCII come Ésono codificati (usando od -xa) come 0xC9, quindi suppongo che sarebbe ISO-8859-1.
Albert,

@Albert, probabilmente. Puoi usare locale -aper vedere se ci sono impostazioni locali con iso8859-1 come set di caratteri sul tuo sistema e usare LC_CTYPE=<that-locale> tr ...[:print:]...per convertire non stampabili in quella locale. Oppure puoi usare iconv per convertire quei file nel set di caratteri della tua locale.
Stéphane Chazelas,

Penso che non sia necessario, perché il set di caratteri del mio locale è impostato su LC_ALL=en_US.iso88591. Quindi, il tuo comando ( tr -c '[:print:]\t\r\n' '[ *]') funziona perfettamente senza modificare le impostazioni locali o convertire il file. Molte grazie.
Albert,

0

Stavo cercando di inviare una notifica tramite libnotify, con contenuti che potrebbero contenere caratteri non stampabili. Le soluzioni esistenti non hanno funzionato del tutto per me (usando una whitelist di caratteri usando trworks, ma eliminando qualsiasi carattere multi-byte).

Ecco cosa ha funzionato, superando il test 💩:

message=$(iconv --from-code=UTF-8 -c <<< "$message")
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.