วิธี grep สำหรับ unicode ในสคริปต์ทุบตี


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

โดยทั่วไปหากไฟล์ "out.txt" มี " " ที่ใดก็ได้ในไฟล์ฉันต้องการให้เสียงก้อง "ทำงาน" และถ้าไฟล์ "out.txt" นั้นไม่มี " " ที่ใดก็ตามในไฟล์ฉันก็อยาก มันเพื่อ cat out.txt

แก้ไข: ดังนั้นนี่คือสิ่งที่ฉันทำ ฉันกำลังพยายามที่จะเปิดใช้งานการถอดรหัส openssl

openssl enc ส่งคืนค่า 0 เมื่อสำเร็จไม่ใช่ค่าศูนย์ หมายเหตุ: คุณจะได้รับผลบวกปลอมเนื่องจาก AES / CBC สามารถกำหนดได้ว่า "การถอดรหัสทำงานได้" หรือไม่ขึ้นอยู่กับการรับสิทธิ์ ดังนั้นไฟล์ถอดรหัส แต่มันจะไม่เป็นรหัสผ่านที่ถูกต้องดังนั้นมันจะมีซึ่งพูดพล่อยๆในนั้น อักขระทั่วไปในการพูดพล่อยๆคือ " " ดังนั้นฉันต้องการให้วนรอบทำต่อไปถ้าผลลัพธ์มี "contains"

นี่คือลิงค์คอมไพล์ของฉันhttps://github.com/Raphaeangelo/OpenSSLCracker นี่คือสคริปต์

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

มันยังคงแสดงเอาท์พุทให้ฉันด้วย charicter

อัปเดต: แก้ไขแล้ว

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

มันดูถูกต้องควรใช้งานได้ (btw ฉันไม่มีฟอนต์ให้เห็นอักขระยูนิโค้ดของคุณ แต่ไม่มีสิ่งใดมีความหมายพิเศษ) grepเข้าใจ unicode นาน (ซึ่งทำให้ช้าลงมากดังนั้นในการค้นหาสตริง ascii a LANG=C grepเป็นการปรับปรุงประสิทธิภาพครั้งใหญ่)
peterh - Reinstate Monica

ฉันอาจต้องลบและโพสต์คำถามอื่นเพราะฉันแน่ใจว่าฉันสับสนทุกคนที่นี่
Stuart Sloan

@ Stuart Sloan ชื่อคำถามของคุณHow to grep for unicode � in a bash scriptคือจริง ๆ แล้วคุณต้องการอะไร แตกยูนิโคด? โปรดอธิบายเพื่อให้เราสามารถช่วยได้!

1
@Goro ฉันได้แก้ไขโพสต์ต้นฉบับของฉันแล้ว ฉันหวังว่ามันสมเหตุสมผล โปรดแจ้งให้เราทราบหากไม่เป็นเช่นนั้นและฉันจะพยายามชี้แจง
Stuart Sloan

1
คำตอบที่นำเสนอทั้งสองอย่างนี้ทำให้เข้าใจผิดอย่างยิ่ง โปรดอ่าน (อีกครั้ง) คำตอบของฉันฉันได้แก้ไขมันเพื่ออธิบายว่ามันผิดกับทั้งคำตอบ
ไอแซค

คำตอบ:


27

grep เป็นเครื่องมือที่ผิดสำหรับงาน

คุณเห็นเครื่องหมาย U+FFFD REPLACEMENT CHARACTERไม่ใช่เพราะมันอยู่ในเนื้อหาไฟล์อย่างแท้จริง แต่เนื่องจากคุณดูที่ไฟล์ไบนารีด้วยเครื่องมือที่ควรจัดการกับอินพุตข้อความเท่านั้น วิธีมาตรฐานในการจัดการอินพุตที่ไม่ถูกต้อง (เช่นข้อมูลไบนารีแบบสุ่ม) คือการแทนที่ทุกอย่างที่ไม่ถูกต้องในสถานที่ปัจจุบัน (ส่วนใหญ่อาจเป็น UTF-8) ด้วย U + FFFD ก่อนที่จะเข้าชมหน้าจอ

ซึ่งหมายความว่ามีความเป็นไปได้อย่างมากที่ลำดับตัวอักษร\xEF\xBF\xBD(ลำดับ UTF-8 สำหรับอักขระ U + FFFD) จะไม่เกิดขึ้นในไฟล์ grepถูกต้องสมบูรณ์ในการบอกคุณไม่มี

วิธีหนึ่งในการตรวจสอบว่าไฟล์มีไบนารี่ที่ไม่รู้จักอยู่บ้างหรือไม่ด้วยfile(1)คำสั่ง:

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

สำหรับไฟล์ประเภทใด ๆ dataที่ไม่รู้จักมันก็จะบอกว่า ลอง

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

เพื่อตรวจสอบว่าไฟล์มีไบนารีแบบสุ่มใด ๆ หรือไม่และมีแนวโน้มว่าจะเป็นขยะมากที่สุด

หากคุณต้องการให้แน่ใจว่าout.txtเป็นไฟล์ข้อความที่เข้ารหัส UTF-8 เท่านั้นคุณสามารถใช้iconv:

$ iconv -f utf-8 -t utf-16 out.txt >/dev/null

คุณพูดถูก! น่าเสียดายที่ฉันยังคงได้รับขยะ (น้อยกว่าก่อน) ในผลลัพธ์
Stuart Sloan

อาจfileตรวจจับเนื้อหาประเภทอื่น ๆ สำหรับไฟล์เหล่านั้น หากคุณ 100% เสมอเพียงคาดหวัง UTF-8 ไฟล์ข้อความที่เข้ารหัสคุณสามารถตรวจสอบกับiconvถ้าแฟ้มถูกต้อง iconv -f utf-8 -t utf-16 out.txt >/dev/nullUTF-8: หากiconvไม่สามารถแปลงไฟล์ได้เนื่องจากลำดับ UTF-8 ที่ไม่ถูกต้องไฟล์นั้นจะกลับมาพร้อมกับรหัสออกที่ไม่ใช่ศูนย์
Boldewyn

2
คำสั่งไฟล์ถูกต้อง! คุณช่วยฉันแก้ปัญหาของฉันขอบคุณ!
Stuart Sloan

4
แน่นอนว่า grep "เป็นเครื่องมือสำหรับงาน" grep -axv '.*' badchars.txtลอง ที่จะพิมพ์บรรทัดใด ๆ ที่มีใด ๆ ที่ไม่ถูกต้องUnicodeตัวอักษร
Isaac

1
นี่เป็นเรื่องเข้าใจผิดอย่างยิ่งโปรดอ่านคำตอบของฉันเกี่ยวกับสิ่งที่fileทำ
ไอแซค

5

TL; DR:

grep -axv '.*' out.txt 

คำตอบยาว

คำตอบปัจจุบันทั้งสองมีความเข้าใจผิดอย่างมากและผิดโดยทั่วไป

ในการทดสอบรับไฟล์สองไฟล์นี้ (จากนักพัฒนาที่ได้รับการยอมรับเป็นอย่างดี: Markus Kuhn):

$ 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

การสาธิต

สิ่งแรกUTF-8-demo.txtคือไฟล์ที่ถูกออกแบบมาเพื่อแสดงว่า UTF-8 สามารถนำเสนอภาษา, คณิตศาสตร์, อักษรเบรลล์และอักขระที่มีประโยชน์อื่น ๆ อีกมากมายได้อย่างไร ลองดูด้วยโปรแกรมแก้ไขข้อความ (นั่นเข้าใจ UTF-8) และคุณจะเห็นจำนวนมากตัวอย่างและไม่มี

การทดสอบที่คำตอบเดียวเสนอ: เพื่อ จำกัด ช่วงของตัวอักษรเพื่อ\x00-\x7Fจะปฏิเสธเกือบทุกอย่างภายในไฟล์นี้
มันผิดมากและจะไม่ลบไฟล์ใด ๆเนื่องจากไม่มีอยู่ในไฟล์นั้น

การใช้การทดสอบที่แนะนำในคำตอบนั้นจะเป็นการลบ72.5 %ไฟล์:

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

นั่นคือ (สำหรับการใช้งานจริง) ไฟล์ทั้งหมด ไฟล์ที่ออกแบบมาอย่างดีเพื่อแสดงอักขระที่ใช้ได้อย่างสมบูรณ์แบบ

ทดสอบ

ไฟล์ที่สองได้รับการออกแบบมาเพื่อทดลองใช้เคสขนาดเล็กเพื่อยืนยันว่าเครื่องอ่าน utf-8 ทำงานได้ดี มันมีอยู่ภายในตัวละครมากมายที่จะทำให้ ' ' ที่จะแสดง แต่ข้อเสนอแนะคำตอบอื่น ๆ (อันที่เลือก) เพื่อใช้fileล้มเหลวอย่างไม่มีการลดกับไฟล์นี้ เฉพาะการลบศูนย์ไบต์ ( \0) (ซึ่งเป็นเทคนิคที่ถูกต้อง ASCII) และ\x7fไบต์ (DEL - ลบ) (ซึ่งเป็นอักขระ ASCII อย่างชัดเจนเช่นกัน) จะทำให้ไฟล์ทั้งหมดที่ถูกต้องสำหรับfileคำสั่ง:

$ 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

ไม่เพียง แต่fileตรวจไม่พบอักขระที่ไม่ถูกต้องจำนวนมากแต่ยังไม่สามารถตรวจจับและรายงานว่าเป็นไฟล์ที่เข้ารหัส UTF-8

และใช่fileสามารถตรวจจับและรายงานข้อความที่เข้ารหัส UTF-8 ได้:

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

นอกจากนี้fileไม่สามารถรายงานเป็น ASCII ของอักขระควบคุมส่วนใหญ่ในช่วง 1 ถึง 31 ได้ ( file) รายงานบางช่วงdataดังนี้:

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

คนอื่น ๆ เป็นASCII text:

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

เป็นช่วงอักขระที่พิมพ์ได้ (พร้อมบรรทัดใหม่):

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

แต่บางช่วงอาจทำให้ได้ผลลัพธ์ที่แปลก:

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

โปรแกรมfileไม่ได้เป็นเครื่องมือในการตรวจจับข้อความ แต่เพื่อตรวจจับหมายเลขเวทย์มนตร์ในโปรแกรมหรือไฟล์ปฏิบัติการ

ช่วงการfileตรวจจับและประเภทที่สัมพันธ์กันที่ฉันรายงานพบคือ:

  • หนึ่งไบต์ค่าส่วนใหญ่ 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
    
  • ช่วงที่เข้ารหัส 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
    

ทางออกหนึ่งที่เป็นไปได้อยู่ด้านล่าง


คำตอบก่อนหน้า

ค่า Unicode สำหรับตัวละครที่คุณกำลังโพสต์คือ:

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

ใช่ว่าเป็นอักขระ Unicode 'แทนอักขระตัว' (U + FFFD) นั่นคืออักขระที่ใช้เพื่อแทนที่อักขระ Unicode ที่ไม่ถูกต้องที่พบในข้อความ มันเป็น "เครื่องช่วยมองเห็น" ไม่ใช่ตัวละครที่แท้จริง ในการค้นหาและแสดงรายการทุกบรรทัดที่มีอักขระUNICODE ที่ไม่ถูกต้องให้ใช้:

grep -axv '.*' out.txt 

แต่ถ้าคุณต้องการตรวจสอบว่าอักขระใดไม่ถูกต้องให้ใช้:

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

หากผลเป็นไฟล์ที่สะอาดมิฉะนั้นจะเป็นศูนย์10


หากสิ่งที่คุณถามคือวิธีการค้นหาตัวละครแล้วใช้สิ่งนี้:

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

หรือหากระบบของคุณประมวลผลข้อความ UTF-8 อย่างถูกต้องเพียง:

➤ echo "$a" | grep -oP '�'
�

OMG ขอบคุณมากสำหรับgrep -axv '.*' !! ฉันได้ต่อสู้กับตัวละครที่ไม่ดีในไฟล์ข้อความของฉันและวิธีการแก้ไขพวกเขาใน emacs เป็นเวลาหนึ่งหรือสองปี !!!
nealmcb

3

คำตอบแรกนี้สำหรับโพสต์ต้นฉบับซึ่ง:

วิธี grep สำหรับ unicode ในสคริปต์ทุบตี

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

โดยทั่วไปหากไฟล์ "out.txt" มี " " ที่ใดก็ได้ในไฟล์ฉันต้องการให้เสียงก้อง "ทำงาน" และถ้าไฟล์ "out.txt" นั้นไม่มี " " ที่ใดก็ตามในไฟล์ฉันก็อยาก มันเพื่อ cat out.txt

ลอง

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

ด้วยif .. thenคำสั่งดังต่อไปนี้:

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

Explanation💡:

  • -P, --perl-regexp: PATTERN เป็นนิพจน์ปกติของ Perl
  • -o, --only-matching: แสดงเฉพาะส่วนของการจับคู่บรรทัด PATTERN
  • [^\x00-\x7F] เป็น regex เพื่อจับคู่อักขระที่ไม่ใช่ ASCII เดี่ยว
  • [[:ascii:]] - จับคู่อักขระ ASCII เดียว
  • [^[:ascii:]] - ตรงกับอักขระที่ไม่ใช่ ASCII ตัวเดียว

ใน bash

LC_COLLATE=C grep -o '[^ -~]' file

3
สิ่งนี้จะพัง (มีข้อผิดพลาดที่เป็นบวก) ทันทีที่ใครบางคนไม่พูดภาษาอังกฤษ ...
Kevin

หรือถ้ามีคนพยายามพูดถึงà la carte, emoji, Pokémonหรืออะไรก็ตามที่ไม่ได้ จำกัด อยู่ที่ 7 บิต ASCII มองหาอะไรที่ดีกว่าใน 00-1F ยกเว้น 09 0A 0D (แท็บ linefeed, carriage return)
Alcaro

นี่เป็นความคิดที่แย่มาก สิ่งนี้จะปฏิเสธอักขระ Unicode ที่ถูกต้องใด ๆที่อยู่เหนือช่วง ASCII เพียงอักขระที่ถูกต้องมากกว่าหนึ่งล้านตัวเท่านั้น น่าอัศจรรย์ ลอง: printf '%b' "$(printf '\\U%x' {128..131})" | grep -oP "[^\x00-\x7F]"เพียง 4 ตัวอักษร Unicode ที่ถูกต้องซึ่งโค้ดของคุณปฏิเสธ :-(
Isaac

นี่เป็นคำตอบที่ทำให้เข้าใจผิดอย่างมาก โปรดอ่านในคำตอบของฉันว่าทำไมวิธีแบบง่าย ๆ ในการ จำกัด เฉพาะ ASCII จึงล้มเหลวอย่างไม่มีการลด
ไอแซค
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.