Jak grep dla Unicode w skrypcie bash

11
if grep -q "�" out.txt
    then
        echo "working"
    else
        cat out.txt
fi

Zasadniczo, jeśli plik „out.txt” zawiera „ ” w dowolnym miejscu pliku, chciałbym, aby echo „działało” ORAZ jeśli plik „out.txt” NIE zawiera nigdzie w pliku „ ”, to chciałbym to cat out.txt

EDYCJA: Więc oto co robię. Próbuję brutalną siłą odszyfrować openssl.

openssl enc zwraca 0 w przypadku sukcesu, w przeciwnym razie niezerowe. Uwaga: otrzymasz fałszywe alarmy, ponieważ AES / CBC może jedynie ustalić, czy „deszyfrowanie działa” w oparciu o prawidłowe wypełnienie. Plik jest odszyfrowywany, ale nie będzie to prawidłowe hasło, więc będzie w nim bełkot. Typową postacią w bełkocie jest „ ”. Dlatego chcę, aby pętla do kontynuowała działanie, jeśli wynik zawiera „ ”.

Oto mój link do git https://github.com/Raphaeangelo/OpenSSLCracker Heres the script

while read line
do
openssl aes-256-cbc -d -a -in $1 -pass pass:$line -out out.txt 2>out.txt >/dev/null && printf "==================================================\n"
if grep -q "�" out.txt
    then
        :
    else
        cat out.txt &&
            printf "\n==================================================" &&
            printfn"\npassword is $line\n" &&
            read -p "press return key to continue..." < /dev/tty; 
fi
done < ./password.txt

wciąż pokazuje mi wynik z charakterem

AKTUALIZACJA: rozwiązana

printf "Working..."

while read line
do
openssl aes-256-cbc -d -a -in $1 -pass pass:$line -out out.txt 2>out.txt >/dev/null
if file out.txt | grep -q 'out.txt: ASCII text'
    then
        printf "\n==================================================\n\n" &&
            cat out.txt &&
            printf "\n==================================================" &&
            printf "\npassword is $line\n" && 
            read -p "press return key to continue..." < /dev/tty;
    else
        : 
fi
done < ./password.txt
Stuart Sloan
źródło
Wygląda poprawnie, powinien działać (btw, nie mam czcionki dla twojej postaci Unicode do zobaczenia, ale żadna z nich nie ma żadnego specjalnego znaczenia). grepdługo rozumie Unicode (co czyni go znacznie wolniejszym, więc wyszukiwanie ciągów ascii LANG=C grepjest ogromną poprawą wydajności).
peterh - Przywróć Monikę
Być może będę musiał to usunąć i opublikować kolejne pytanie, ponieważ jestem pewien, że całkowicie wprowadzam wszystkich w błąd.
Stuart Sloan,
@Stuart Sloan tytuł twojego pytania brzmi: How to grep for unicode � in a bash scriptczy naprawdę tego chcesz? wyodrębnić Unicode? prosimy o wyjaśnienie, abyśmy mogli pomóc!
1
@Goro Dokonałem edycji mojego oryginalnego postu. Mam nadzieję, że to ma sens. Daj mi znać, jeśli tak się nie stanie, a ja postaram się wyjaśnić.
Stuart Sloan,
1
Obie obecne odpowiedzi są bardzo mylące. Proszę przeczytać (jeszcze raz) moją odpowiedź , edytowałem ją, aby wyjaśnić, że waht jest niepoprawny w przypadku obu odpowiedzi.
Izaak

Odpowiedzi:

27

grep jest niewłaściwym narzędziem do pracy.

Widzisz U+FFFD REPLACEMENT CHARACTERnie dlatego, że jest dosłownie w treści pliku, ale dlatego, że spojrzałeś na plik binarny za pomocą narzędzia, które powinno obsługiwać tylko wprowadzanie tekstu. Standardowym sposobem obsługi nieprawidłowych danych wejściowych (tj. Losowych danych binarnych) jest zastąpienie wszystkiego, co nie jest poprawne w bieżących ustawieniach regionalnych (najprawdopodobniej UTF-8), U + FFFD, zanim trafi ono na ekran.

Oznacza to, że jest bardzo prawdopodobne, że literał \xEF\xBF\xBD(sekwencja bajtów UTF-8 dla znaku U + FFFD) nigdy nie występuje w pliku. grepma rację mówiąc, że nie ma.

Jednym ze sposobów wykrycia, czy plik zawiera jakiś nieznany plik binarny, jest file(1)polecenie:

$ head -c 100 /dev/urandom > rubbish.bin
$ file rubbish.bin
rubbish.bin: data

W przypadku każdego nieznanego typu pliku będzie to po prostu powiedzieć data. Próbować

$ file out.txt | grep '^out.txt: data$'

aby sprawdzić, czy plik naprawdę zawiera dowolny dowolny plik binarny, a tym samym najprawdopodobniej śmieci.

Jeśli chcesz się upewnić, że out.txtjest to tylko plik tekstowy zakodowany w UTF-8, możesz alternatywnie użyć iconv:

$ iconv -f utf-8 -t utf-16 out.txt >/dev/null
Boldewyn
źródło
Masz rację! niestety nadal otrzymuję jakieś (mniej niż wcześniej) śmieci na wyjściu.
Stuart Sloan,
Prawdopodobnie filewykrywa jakiś inny typ zawartości dla tych plików. Jeśli 100% zawsze tylko oczekiwać kodowanie UTF-8 pliki tekstowe, można sprawdzić u iconv, jeśli plik jest ważny UTF-8: iconv -f utf-8 -t utf-16 out.txt >/dev/null. Jeśli iconvnie można przekonwertować pliku z powodu nieprawidłowych sekwencji UTF-8, zostanie zwrócony z niezerowym kodem wyjścia.
Boldewyn,
2
Polecenie pliku miało rację! Pomogłeś mi rozwiązać mój problem, dzięki!
Stuart Sloan,
4
Oczywiście ten grep „jest narzędziem do pracy”, spróbuj grep -axv '.*' badchars.txt. Spowoduje to wydrukowanie dowolnego wiersza zawierającego dowolny nieprawidłowy znak Unicode .
Izaak
1
Jest to bardzo mylące, proszę przeczytać w mojej odpowiedzi o tym, co filerobi.
Izaak
5

TL; DR:

grep -axv '.*' out.txt 

długa odpowiedź

Obie obecne odpowiedzi są bardzo mylące i zasadniczo błędne.

Aby przetestować, pobierz te dwa pliki (od bardzo dobrze uznanego programisty: Markusa Kuhna):

$ wget https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-demo.txt
$ wget https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt

Próbny

Pierwszy UTF-8-demo.txtto plik zaprojektowany, aby pokazać, jak dobrze UTF-8 jest w stanie przedstawić wiele języków, matematykę, alfabet Braille'a i wiele innych przydatnych typów znaków. Spójrz za pomocą edytora tekstu (który rozumie utf-8), a zobaczysz wiele przykładów i nie .

Test zaproponowany przez jedną odpowiedź: ograniczenie zakresu znaków do \x00-\x7Fodrzucenia prawie wszystkiego w tym pliku.
To bardzo źle i nie usunie żadnego, ponieważ nie ma go w tym pliku .

Zastosowanie testu zalecanego w tej odpowiedzi spowoduje usunięcie 72.5 %pliku:

$ grep -oP "[^\x00-\x7F]" UTF-8-demo.txt | tr -d '\n' | wc -c
10192
$ cat UTF-8-demo.txt | wc -c
14058

To jest (dla najbardziej praktycznych celów) cały plik. Plik bardzo dobrze zaprojektowany, aby wyświetlać idealnie poprawne znaki.

Test

Drugi plik ma na celu wypróbowanie kilku przypadków granicznych w celu potwierdzenia, że ​​czytniki utf-8 wykonują dobrą robotę. Zawiera wiele znaków, które powodują wyświetlenie znaku . Ale druga rekomendacja odpowiedzi (wybrana) filezawodzi rażąco z tym plikiem. Tylko usunięcie zerowego bajtu ( \0) (który technicznie jest poprawny ASCII) i \x7fbajtu (DEL - usuń) (który oczywiście jest również znakiem ASCII) sprawi, że cały plik będzie ważny dla filepolecenia:

$ cat UTF-8-test.txt | tr -d '\0\177' > a.txt
$ file a.txt 
a.txt: Non-ISO extended-ASCII text, with LF, NEL line terminators

Nie tylko nie filewykrywa wielu niepoprawnych znaków, ale także nie wykrywa i nie zgłasza, że ​​jest to plik zakodowany w UTF-8.

I tak, filejest w stanie wykryć i zgłosić tekst zakodowany w UTF-8:

$ echo "ééakjfhhjhfakjfhfhaéá" | file -
/dev/stdin: UTF-8 Unicode text

Ponadto filenie zgłasza jako ASCII większości znaków kontrolnych z zakresu od 1 do 31. fileZgłasza niektóre zakresy jako data:

$ printf '%b' "$(printf '\\U%x' {1..6})" | file -
/dev/stdin: data

Inne jako ASCII text:

$ printf '%b' "$(printf '\\U%x' 7 {9..12})" | file -
/dev/stdin: ASCII text

Jako zakres znaków do wydruku (z nowymi liniami):

$ printf '%b' "$(printf '\\U%x' {32..126} 10)" | file -
/dev/stdin: ASCII text

Ale niektóre zakresy mogą powodować dziwne wyniki:

$ printf '%b' "$(printf '\\U%x' {14..26})" | file -
/dev/stdin: Atari MSA archive data, 4113 sectors per track, starting track: 5141, ending track: 5655

Program filenie jest narzędziem do wykrywania tekstu, ale do wykrywania magicznych liczb w wykonywalnych programach lub plikach.

Wykryto zakresy filei odpowiedni typ zgłoszony przeze mnie znaleziony to:

  • Wartości jednobajtowe, głównie ascii:

    {1..6} {14..26} {28..31} 127   :data
    {128..132} {134..159}          :Non-ISO extended-ASCII text
    133                            :ASCII text, with LF, NEL line terminators
    27                             :ASCII text, with escape sequences
    13                             :ASCII text, with CR, LF line terminators
    8                              :ASCII text, with overstriking
    7 {9..12} {32..126}            :ASCII text
    {160..255}                     :ISO-8859 text
    
  • Zakodowane zakresy Utf-8:

    {1..6} {14..26} {28..31} 127   :data
    27                             :ASCII text, with escape sequences
    13                             :ASCII text, with CR, LF line terminators
    8                              :ASCII text, with overstriking
    7 {9..12} {32..126}            :ASCII text
    {128..132} {134..159}          :UTF-8 Unicode text
    133                            :UTF-8 Unicode text, with LF, NEL line terminators
    {160..255}                     :UTF-8 Unicode text
    {256..5120}                    :UTF-8 Unicode text
    

Jedno z możliwych rozwiązań znajduje się poniżej.


Poprzednia odpowiedź.

Wartość Unicode publikowanego znaku to:

$ printf '%x\n' "'�"
fffd

Tak, jest to znak Unicode „ZMIANA WYMIANY” (U + FFFD) . Jest to znak używany do zastąpienia dowolnego nieprawidłowego znaku Unicode znalezionego w tekście. To „pomoc wizualna”, a nie prawdziwa postać. Aby znaleźć i wyświetlić każdą pełną linię zawierającą nieprawidłowe znaki UNICODE, użyj:

grep -axv '.*' out.txt 

ale jeśli chcesz tylko wykryć, czy jakiś znak jest nieprawidłowy, użyj:

grep -qaxv '.*' out.txt; echo $?

Jeśli wynikiem jest to, 1że plik jest czysty, w przeciwnym razie wyniesie zero 0.


Jeśli pytałeś: jak znaleźć postać, użyj tego:

➤ a='Basically, if the file "out.txt" contains "�" anywhere in the file I'
➤ echo "$a" | grep -oP $(printf %b \\Ufffd)
�

Lub jeśli twój system poprawnie przetwarza tekst UTF-8, po prostu:

➤ echo "$a" | grep -oP '�'
�
Izaak
źródło
OMG, dziękuję bardzo za grep -axv '.*' !! Walczyłem z kilkoma złymi znakami w moich plikach tekstowych i jak je naprawić w emacsie, przez dekadę lub dwie !!!
nealmcb
3

Ta bardzo wczesna odpowiedź dotyczyła oryginalnego postu, który brzmiał:

Jak grep dla Unicode w skrypcie bash

if grep -q "�" out.txt
    then
        echo "working"
    else
        cat out.txt  fi

Zasadniczo, jeśli plik „out.txt” zawiera „ ” w dowolnym miejscu pliku, chciałbym, aby echo „działało” ORAZ jeśli plik „out.txt” NIE zawiera nigdzie w pliku „ ”, to chciałbym to cat out.txt

Próbować

grep -oP "[^\x00-\x7F]"

z następującym if .. thenoświadczeniem:

if grep -oP "[^\x00-\x7F]" file.txt; then
    echo "grep found something ..."
else
    echo "Nothing found!"
fi

Objaśnienie💡:

  • -P, --perl-regexp: WZÓR jest wyrażeniem regularnym Perla
  • -o, --only-matching: pokaż tylko część linii pasującą do WZORCA
  • [^\x00-\x7F] jest wyrażeniem regularnym pasującym do pojedynczego znaku spoza ASCII.
  • [[:ascii:]] - dopasowuje pojedynczy znak ASCII
  • [^[:ascii:]] - dopasowuje pojedynczy znak spoza ASCII

w bash

LC_COLLATE=C grep -o '[^ -~]' file
Toby Speight
źródło
3
To się zepsuje (fałszywie pozytywne), gdy tylko ktoś nie będzie mówić po angielsku ...
Kevin
lub jeśli ktoś próbuje omówić à la carte, emoji, Pokémon lub cokolwiek innego, co nie ogranicza się ściśle do 7bit ASCII. Lepiej szukaj czegokolwiek w 00-1F, z wyjątkiem 09 0A 0D (tab, podawanie wiersza, powrót karetki).
Alcaro,
To bardzo zły pomysł. Odrzuci to dowolny prawidłowy znak Unicode powyżej zakresu ASCII, tylko nieco ponad milion poprawnych znaków. Niesamowity. Spróbuj: printf '%b' "$(printf '\\U%x' {128..131})" | grep -oP "[^\x00-\x7F]"tylko 4 prawidłowe znaki Unicode, które odrzuca Twój kod. :-(
Isaac
To bardzo myląca odpowiedź. Proszę przeczytać w mojej odpowiedzi, dlaczego uproszczone podejście ograniczania się tylko do ASCII zawodzi rażąco.
Izaak