Usuwanie wszystkich znaków innych niż ascii z przepływu pracy (pliku)

13

Jak usunąć wszystkie znaki inne niż ascii z jednego pliku? Czy byłoby jakieś specjalne polecenie, aby to zrobić?

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

Wierzę, że znajdzie to znaki w przepływie pracy, ale jak mam usunąć wszystkie wystąpienia tych znaków?

Mizole Ni
źródło
2
powiązane: jeśli chcesz po prostu uniknąć problemów z cat -vznakami kontrolnymi (zamiast po cichu się ich pozbyć), możesz po prostu użyć, aby pokazać je w represantacji ASCII dla nich. (np. ^Gdla \007)
Matija Nalis
1
Kiedy mówisz „znaki nieazjatyckie”, czy obejmujesz również znaki akcentowane?
Captain Man
1
@MatijaNalis Więcej informacji o reprezentacji: en.wikipedia.org/wiki/Caret_notation
wjandrea
1
Jaki jest przypadek użycia? Bardzo często istnieją specjalne narzędzia lub różne podejścia, które działają znacznie lepiej niż zwykłe usuwanie wielu znaków specjalnych. Pamiętaj, że ASCII zawiera kilka „znaków specjalnych”, takich jak pionowe tabulatory, dzwonek i NUL - czy na pewno nie masz na myśli znaków do wydrukowania ?
l0b0

Odpowiedzi:

26

Znaki ASCII to znaki z zakresu od 0 do 177 (ósemkowe) włącznie .

Aby usunąć znaki spoza tego zakresu w pliku, użyj

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

trPolecenie to narzędzie, które działa na pojedynczych znaków , albo zastępując je innymi pojedynczych znaków (transliteracji), usuwając je lub ściskania serie tego samego znaku w jednej postaci.

Powyższe polecenie odczytuje filei zapisuje zmodyfikowaną treść newfile. -dOpcja trsprawia użytkowych znaków Delete (zamiast nich transliteracji) i -csprawia, że pod uwagę znaki poza danym przedziale (zamiast wewnętrznej).

LC_ALL=Cupewnia się, że każda wartość bajtu stanowi poprawny znak. Bez tego niektóre trimplementacje przerwałyby się, gdyby znalazły sekwencje bajtów, które nie tworzą prawidłowych znaków w kodowaniu znaków ustawień regionalnych.


Aby zastąpić oryginalny plik zmodyfikowanym, użyj

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

Spowoduje to zmianę nazwy nowego pliku na nazwę starego pliku po trpomyślnym zakończeniu. Jeśli trnie zakończy się pomyślnie, ponieważ nie można odczytać oryginalnego pliku lub nie zapisać do nowego pliku, oryginalny plik pozostanie niezmieniony.

Alternatywnie, aby zachować jak najwięcej metadanych (uprawnień itp.) Oryginalnego pliku, użyj

cp file tmpfile &&
LC_ALL=C tr -dc '\0-\177' <tmpfile >file &&
rm tmpfile
Kusalananda
źródło
14

Z perl

perl -pi -e 's/[^[:ascii:]]//g'
NotAnUnixNazi
źródło
9

Jeśli wszystko, czego potrzebujesz, to wyrażenie regularne: [\x00-\x7F]które możesz zastosować do kilku narzędzi:

<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'

Zrozum, że sed, awk i perl oczekują „plików tekstowych” zdefiniowanych w Uniksie. W tym przypadku wszystko działa dobrze. Ale w szczególności awk dodaje końcową nową linię (niezależnie od tego, czy istniała w pliku źródłowym, czy nie) (użycie printf usuwa WSZYSTKIE nowe linie na wejściu). Tr jest przeznaczony do pracy z dowolnym typem pliku. Jednak NUL ( \0) nie jest prawidłowym znakiem w pliku tekstowym POSIX i należy go unikać:

Linie nie zawierają znaków NUL ...

W rzeczywistości wiele znaków kontrolnych generowałoby inne problemy w określonych warunkach.
Więc prawdopodobnie potrzebujesz[\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'

Zakres 7-13 (dziesiętnie) to \a\b\t\n\v\f\r(w kolejności).
Podobny (prawdopodobnie bardziej przenośny) zakres można zapisać jako [^[:space:][:print:]] (similar because it doesn't include\ a \ b` --bell i 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:]'

Powiązane:
Regeksuj dowolny znak ASCII
Rozwiązanie Perl
Posix Plik tekstowy

NotAnUnixNazi
źródło
Pamiętaj, że wejściem trmoże być dowolny typ pliku, a nie tylko pliki tekstowe. awkz drugiej strony pobiera plik tekstowy.
Kusalananda
Bardzo trudno jest mi znaleźć coś innego, co nazwałbym plik „tylko znakami ascii” niczym innym jak „plikiem tekstowym” (tak, tak: laik). @Kusalananda (i tak dodano notatkę o awk).
NotAnUnixNazi
Zauważ, że gensub()jest to rozszerzenie gawk. Chciałbyś gsub(...); printi użyj ósemkowej zamiast sekwencji szesnastkowych (i LC_ALL = C), aby być (więcej) przenośnym.
Stéphane Chazelas
@ StéphaneChazelas Jakie jest ograniczenie GNU sed, które sprawia, że ​​składnia GNU jest specyficzna (rozumiem problem POSIXLY_CORRECT).
NotAnUnixNazi
[^\o0]polega na dopasowaniu znaków innych niż ukośnik odwrotny, o i 0 w POSIX sed(we wszystkich implementacjach oprócz GNU sed). To nie jest ograniczenie GNU, sedale rozszerzenie niezgodne, dlatego jest ono wyłączone, gdy POSIXLY_CORRECT znajduje się w środowisku).
Stéphane Chazelas