किसी फ़ाइल में बाइट अनुक्रम होने की संख्या को मैं कैसे गिन सकता हूं?


16

मैं गिनना चाहता हूं कि बाइट का एक निश्चित क्रम एक फ़ाइल के अंदर कितनी बार होता है। उदाहरण के लिए, मैं यह पता लगाना चाहता हूं कि \0xdeadbeefनिष्पादन योग्य फ़ाइल के अंदर संख्या कितनी बार होती है। अभी मैं grep का उपयोग कर रहा हूं:

#/usr/bin/fish
grep -c \Xef\Xbe\Xad\Xde my_executable_file

(बाइट्स उल्टे क्रम में लिखे गए हैं क्योंकि मेरा सीपीयू थोड़ा-सा एंडियन है)

हालाँकि, मुझे अपने दृष्टिकोण से दो समस्याएँ हैं:

  • वे \Xnnबच के क्रम केवल मछली के खोल में काम करते हैं।
  • grep वास्तव में उन लाइनों की संख्या की गिनती कर रहा है जिनमें मेरा मैजिक नंबर है। यदि पैटर्न एक ही पंक्ति में दो बार होता है तो यह केवल एक बार ही गिना जाएगा।

क्या इन समस्याओं को ठीक करने का कोई तरीका है? मैं इस एक लाइनर को बैश शेल में कैसे बना सकता हूं और फ़ाइल के अंदर पैटर्न कितनी बार होता है, इसकी सही गणना करें?


कुछ मदद: unix.stackexchange.com/q/231213/117549 - विशेष रूप से,grep -o
जेफ स्कालर

1
grep उपयोग करने के लिए गलत उपकरण है। Bgrep या bgrep2 पर विचार करें।
fpmurphy

3
यदि खोज करने का क्रम है 11221122, तो इनपुट पर क्या लौटाया जाना चाहिए 112211221122? 1 या 2?
स्टीफन चेजलस

मैं उस मामले में 2 या 3 मैचों की रिपोर्टिंग के साथ ठीक होगा। जिसे लागू करना सरल होगा।
हुगोमग

जवाबों:


15

यह एक-लाइनर समाधान का अनुरोध किया गया है (हाल के गोले के लिए "प्रक्रिया प्रतिस्थापन"):

grep -o "ef be ad de" <(hexdump -v -e '/1 "%02x "' infile.bin) | wc -l

यदि कोई "प्रक्रिया प्रतिस्थापन" <(…)उपलब्ध नहीं है, तो बस फ़िल्टर के रूप में grep का उपयोग करें:

hexdump -v -e '/1 "%02x "' infile.bin  | grep -o "ef be ad de" | wc -l

नीचे समाधान के प्रत्येक भाग का विस्तृत विवरण दिया गया है।

हेक्स संख्या से बाइट मान:

आपकी पहली समस्या को हल करना आसान है:

उन \ Xnn बच अनुक्रम केवल मछली के खोल में काम करते हैं।

ऊपरी Xको निचले हिस्से में बदलें xऔर प्रिंटफ़ (अधिकांश गोले के लिए) का उपयोग करें:

$ printf -- '\xef\xbe\xad\xde'

या उपयोग करें:

$ /usr/bin/printf -- '\xef\xbe\xad\xde'

उन गोले के लिए जो '\ x' प्रतिनिधित्व को लागू नहीं करना चुनते हैं।

बेशक, हेक्स को अष्टक में अनुवाद करना किसी भी खोल पर (लगभग) काम करेगा:

$ "$sh" -c 'printf '\''%b'\'' "$(printf '\''\\0%o'\'' $((0xef)) $((0xbe)) $((0xad)) $((0xde)) )"'

जहां "$ श" कोई (उचित) शेल है। लेकिन इसे सही ढंग से उद्धृत किया जाना काफी कठिन है।

बाइनरी फाइलें।

सबसे मजबूत समाधान फ़ाइल और बाइट अनुक्रम (दोनों) को कुछ एन्कोडिंग में बदलना है जिसमें विषम चरित्र मानों (नई लाइन) 0x0Aया (नल बाइट) के साथ कोई समस्या नहीं है 0x00। दोनों को "टेक्स्ट फ़ाइलों" को संसाधित करने के लिए डिज़ाइन किए गए औज़ारों के साथ सही ढंग से प्रबंधित करना काफी कठिन है।

बेस 64 जैसा एक परिवर्तन एक वैध लग सकता है, लेकिन यह इस मुद्दे को प्रस्तुत करता है कि प्रत्येक इनपुट बाइट में तीन आउटपुट प्रतिनिधित्व तक हो सकते हैं यदि यह मॉड 24 (बिट्स) की स्थिति का पहला, दूसरा या तीसरा बाइट है।

$ echo "abc" | base64
YWJjCg==

$ echo "-abc" | base64
LWFiYwo=

$ echo "--abc" | base64
LS1hYmMK

$ echo "---abc" | base64        # Note that YWJj repeats.
LS0tYWJjCg==

हेक्स परिवर्तन।

यही कारण है कि सबसे मजबूत परिवर्तन एक होना चाहिए जो प्रत्येक बाइट सीमा पर शुरू होता है, जैसे कि साधारण एचईएक्स प्रतिनिधित्व।
हम इस उपकरण में से किसी एक के साथ फ़ाइल के हेक्स प्रतिनिधित्व के साथ एक फ़ाइल प्राप्त कर सकते हैं:

$ od -vAn -tx1 infile.bin | tr -d '\n'   > infile.hex
$ hexdump -v -e '/1 "%02x "' infile.bin  > infile.hex
$ xxd -c1 -p infile.bin | tr '\n' ' '    > infile.hex

खोज के लिए बाइट अनुक्रम इस मामले में पहले से ही हेक्स में है।
:

$ var="ef be ad de"

लेकिन इसे रूपांतरित भी किया जा सकता था। गोल यात्रा हेक्स-बिन-हेक्स का एक उदाहरण इस प्रकार है:

$ echo "ef be ad de" | xxd -p -r | od -vAn -tx1
ef be ad de

खोज स्ट्रिंग को बाइनरी प्रतिनिधित्व से सेट किया जा सकता है। Od, hexdump, या xxd के ऊपर प्रस्तुत तीन विकल्पों में से कोई भी समकक्ष है। बाइट सीमाओं पर मैच सुनिश्चित करने के लिए रिक्त स्थान शामिल करने के लिए बस सुनिश्चित करें (कोई नीच पारी की अनुमति नहीं है):

$ a="$(printf "\xef\xbe\xad\xde" | hexdump -v -e '/1 "%02x "')"
$ echo "$a"
ef be ad de

यदि बाइनरी फ़ाइल इस तरह दिखती है:

$ cat infile.bin | xxd
00000000: 5468 6973 2069 7320 efbe adde 2061 2074  This is .... a t
00000010: 6573 7420 0aef bead de0a 6f66 2069 6e70  est ......of inp
00000020: 7574 200a dead beef 0a66 726f 6d20 6120  ut ......from a 
00000030: 6269 0a6e 6172 7920 6669 6c65 2e0a 3131  bi.nary file..11
00000040: 3232 3131 3232 3131 3232 3131 3232 3131  2211221122112211
00000050: 3232 3131 3232 3131 3232 3131 3232 3131  2211221122112211
00000060: 3232 0a

फिर, एक साधारण grep खोज मिलान किए गए अनुक्रमों की सूची देगी:

$ grep -o "$a" infile.hex | wc -l
2

एक पंक्ति?

यह सब एक पंक्ति में किया जा सकता है:

$ grep -o "ef be ad de" <(xxd -c 1 -p infile.bin | tr '\n' ' ') | wc -l

उदाहरण के लिए, 11221122एक ही फ़ाइल में खोज करने के लिए इस दो चरणों की आवश्यकता होगी:

$ a="$(printf '11221122' | hexdump -v -e '/1 "%02x "')"
$ grep -o "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ') | wc -l
4

मैच देखने के लिए:

$ grep -o "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ')
3131323231313232
3131323231313232
3131323231313232
3131323231313232

$ grep "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ')

… 0a 3131323231313232313132323131323231313232313132323131323231313232 313132320a


बफरिंग

एक चिंता है कि grep पूरी फ़ाइल को बफ़र कर देगा, और, यदि फ़ाइल बड़ी है, तो कंप्यूटर के लिए एक भारी लोड बनाएं। उसके लिए, हम एक अप्रयुक्त सीड समाधान का उपयोग कर सकते हैं:

a='ef be ad de'
hexdump -v -e '/1 "%02x "' infile.bin  | 
    sed -ue 's/\('"$a"'\)/\n\1\n/g' | 
        sed -n '/^'"$a"'$/p' |
            wc -l

पहली सीड अनबर्डर्ड ( -u) है और इसका उपयोग प्रति मेल स्ट्रिंग के स्ट्रीम पर दो नईलाइनों को इंजेक्ट करने के लिए किया जाता है। दूसरा sedकेवल छोटी (छोटी) मिलान रेखाएँ मुद्रित करेगा। Wc -l मिलान लाइनों को गिनेगा।

यह केवल कुछ छोटी लाइनों को बफ़र करेगा। दूसरे सेड में मैचिंग स्ट्रिंग (एस)। उपयोग किए गए संसाधनों में यह काफी कम होना चाहिए।

या, समझने के लिए कुछ अधिक जटिल है, लेकिन एक सेड में एक ही विचार:

a='ef be ad de'
hexdump -v -e '/1 "%02x "' infile.bin  |
    sed -u '/\n/P;//!s/'"$a"'/\n&\n/;D' |
        wc -l

2
ध्यान दें कि यदि आप सभी पाठ को एक पंक्ति में रखते हैं, तो इसका मतलब है कि grepयह पूरी तरह से मेमोरी में लोड हो जाएगा (यहां हेक्स एन्कोडिंग के कारण मूल फ़ाइल + 1 का आकार दोगुना है), इसलिए अंत में, यह अधिक समाप्त हो रहा है ओवरहेड pythonदृष्टिकोण से या perlएक के साथ -0777। आपको एक grepकार्यान्वयन की भी आवश्यकता है जो मनमानी लंबाई की रेखाओं का समर्थन करता है (जो कि -oआमतौर पर समर्थन करते हैं)। अच्छा जवाब अन्यथा।
स्टीफन चेजलस

1
आपके हेक्स संस्करण निबल-स्थानांतरित मूल्यों से मेल खाते हैं? वांछित बाइट्स के अलावा। od -An -tx1 | tr -d '\n'या hexdump -v -e '/1 " %02x"'एक खोज स्ट्रिंग के साथ, जिसमें रिक्त स्थान होते हैं, इससे बचते हैं, लेकिन मुझे ऐसा कोई निर्धारण नहीं दिखता है xxd
dave_thompson_085 17:16

@ dave_thompson_085 उत्तर संपादित किया गया। मुझे विश्वास है कि उत्तर केवल बाइट सीमाओं से मेल खाएगा, धन्यवाद फिर से।

@ StéphaneChazelas क्या आप एक असंबद्ध सेड का उपयोग करने के प्रस्तावित विकल्प की समीक्षा कर सकते हैं। धन्यवाद।
सोरोन्टर

sed -u(जहां उपलब्ध है) अनबॉर्फ़र के लिए है। इसका मतलब है कि यह इनपुट पर एक बार में एक बाइट पढ़ेगा, और इसका उत्पादन सीधे बफरिंग के बिना आउटपुट देगा। कहीं भी, यह अभी भी पूरी जगह को पैटर्न स्पेस में लोड करने की आवश्यकता होगी, इसलिए यहां मदद नहीं करेगा।
स्टीफन चेजलस

7

जीएनयू grepके -P(perl-regexp) झंडे के साथ

LC_ALL=C grep -oaP '\xef\xbe\xad\xde' file | wc -l

LC_ALL=Cबहु-बाइट स्थानों में समस्याओं से बचने के लिए जहां grepअन्यथा बाइट्स के दृश्यों को वर्णों के रूप में व्याख्या करने की कोशिश करेंगे।

-aबाइनरी फ़ाइलों को पाठ फ़ाइलों के बराबर मानते हैं (सामान्य व्यवहार के बजाय, जहां grepकेवल प्रिंट करता है कि कम से कम एक मैच है या नहीं)


यह समाधान हमेशा सही संख्या के बजाय मुझे 0 मैच दे रहा है।
हुगोमग

@ हगओमग, क्या ऐसा हो सकता है कि आपको grep मैच के लिए इसे प्राप्त करने के लिए पारित बाइट्स को उल्टा करना पड़े ?
इरूवर

मुझे नहीं लगता कि यह आदेश है। इस प्रश्न के अन्य दो उत्तर सही तरीके से काम करते हैं।
हुगोमग

2
@hugomg, यह लोकल है। संपादित देखें।
स्टीफन चेज़लस

2
मैं -aविकल्प को शामिल करने का सुझाव दूंगा , अन्यथा grep Binary file file.bin matchesकिसी भी फ़ाइल के लिए जवाब देगा जो grep बाइनरी के रूप में पता लगाता है।
सोरोंटर

6
PERLIO=:raw perl -nE '$c++ while m/\xef\xbe\xad\xde/g; END{say $c}' file

कौन सा व्यवहार करता है इनपुट फ़ाइल बाइनरी के रूप में (रों) (linefeeds या एन्कोडिंग के लिए कोई अनुवाद, देख perlrun ) तो इनपुट फ़ाइल (रों) दिया हेक्स के सभी मैचों के लिए एक काउंटर incrementing मुद्रण नहीं से अधिक लूप (या जो भी रूप है, को देखने के perlre ) ।


2
ध्यान दें कि यदि आप बाइट 0xa के लिए खोज करने के लिए अनुक्रम का उपयोग नहीं कर सकते हैं। उस स्थिति में, आप एक अलग रिकॉर्ड विभाजक (के साथ -0ooo) का उपयोग कर सकते हैं ।
स्टीफन चेजलस

1
@ स्टेफेनचेलजस आप ब्याज के अनुक्रम का उपयोग खुद के रूप में कर सकते हैं $/, थोड़ा अलग ट्रेडऑफ़ (ऐसे अनुक्रमों के बीच अधिकतम दूरी के लिए आनुपातिक उपयोग) के साथ:perl -nE 'BEGIN { $/ = "\xef\xbe\xad\xde" } chomp; $c++ unless eof && length; END { say $c }'
hobbs

@ StéphaneChazelas कृपया किसी भी बाइट मूल्यों के समाधान के लिए मेरा जवाब पढ़ें।
sorontar

1
@ ओह, किसी भी स्थिति में, यहां तक ​​कि स्मृति उपयोग दो 0xa बाइट्स के बीच अधिकतम दूरी के लिए आनुपातिक होगा जो गैर-पाठ फ़ाइलों के लिए मनमाने ढंग से बड़े हो सकते हैं।
स्टीफन चेजलस

5

GNU के साथ awk, आप यह कर सकते हैं:

LC_ALL=C awk -v 'RS=\xef\xbe\xad\xde' 'END{print NR - (NR && RT == "")}'

यदि बाइट्स में से कोई भी ईआरई ऑपरेटर है, तो उन्हें (हालांकि \\) बच जाना होगा । जैसा 0x2eहै .वैसा प्रवेश करना होगा \\.या \\\x2e। इसके अलावा, यह 0 और 0xa सहित मनमाने बाइट मूल्यों के साथ काम करना चाहिए।

ध्यान दें कि यह उतना सरल नहीं है, NR-1क्योंकि कुछ विशेष मामले हैं:

  • जब इनपुट खाली होता है, NR 0 होता है, NR-1 देता है।
  • जब रिकॉर्ड विभाजक में इनपुट समाप्त होता है, तो उसके बाद एक खाली रिकॉर्ड नहीं बनाया जाता है। हम उस के लिए परीक्षण करते हैं RT==""

यह भी ध्यान दें कि सबसे खराब स्थिति में (यदि फ़ाइल में खोज शब्द नहीं है), तो फ़ाइल पूरी मेमोरी में लोड हो जाएगी)।


5

मेरे द्वारा देखा गया सबसे सीधा-सीधा अनुवाद है:

$ echo $'\xef\xbe\xad\xde' > hugohex
$ echo $'\xef\xbe\xad\xde\xef\xbe\xad\xde' >> hugohex
$ grep -F -a -o -e $'\xef\xbe\xad\xde' hugohex|wc -l
3

मैं कहां से उपयोग किया है $'\xef'के रूप में बैश एएनएसआई-हवाले से (मूल रूप से एक ksh93सुविधा है, अब द्वारा समर्थित zsh, bash, mksh, FreeBSD sh) मछली के संस्करण \Xef, और उपयोग grep -o ... | wc -lउदाहरणों गिनती करने के लिए। grep -oप्रत्येक मैच एक अलग लाइन पर आउटपुट करता है। -aझंडा बाइनरी फ़ाइलें पर उसी तरह यह पाठ फ़ाइलों पर करता है ग्रेप व्यवहार करता है। -Fनिश्चित स्ट्रिंग्स के लिए है ताकि आप regex ऑपरेटरों से बचने की जरूरत नहीं है।

अपने fishमामले की तरह, आप उस दृष्टिकोण का उपयोग नहीं कर सकते हैं, अगर देखने के लिए अनुक्रम में बाइट्स 0 या 0xa (ASCII में नई पंक्ति) शामिल हैं।


का उपयोग printf '%b' $(printf '\\%o ' $((0xef)) $((0xbe)) $((0xad)) $((0xde))) > hugohex'करना सबसे पोर्टेबल "शुद्ध खोल" विधि होगी। बेशक: printf "efbeadde" | xxd -p -r > hugohexसबसे व्यावहारिक विधि की तरह लगता है।
सोरोंटर

4

आप पायथन bytes.countमें गैर-अतिव्यापी सब्सट्रिंग की कुल संख्या प्राप्त करने के लिए पायथन की विधि का उपयोग कर सकते हैं ।

python -c "print(open('./myexecutable', 'rb').read().count(b'\xef\xbe\xad\xde'))"

यह एक-लाइनर पूरे फ़ाइल को मेमोरी में लोड करेगा, इसलिए सबसे कुशल नहीं है, लेकिन काम करता है और पर्ल डी की तुलना में अधिक सुपाठ्य है;


'पर्ल से अधिक सुपाठ्य' टीईसीओ से केवल एक कदम ऊपर है - जो IINM है: 239I$ 190I$ 173I$ 222I$ HXA ERfile$Y 0UC <:S^EQA$; %C$> QC=(gd & r)
dave_thompson_085

आप mmap()पायथन में एक फाइल कर सकते हैं ; यह मेमोरी कमिट को कम करेगा।
टोबी स्पाइट


1

मुझे लगता है कि आप पर्ल का उपयोग कर सकते हैं, इसे आज़माएं:

perl -0777ne 'CORE::say STDOUT s/\xef\xbe\xad\xde//g' file_name  

sबदली हुई कमांड दी गई प्रतिस्थापन की संख्या, -0777 का अर्थ है कि नई लाइन को विशेष वर्ण के रूप में न मानें, e- कमांड निष्पादित करें, sayजो आगे जाता है उसे प्रिंट करने के लिए , फिर नई लाइन चरित्र को प्रिंट करें, nमैंने पूरी तरह से समझ नहीं लिया था, लेकिन w / बाहर काम नहीं करता है - से डॉक्स:

पर्ल आपके प्रोग्राम के आसपास के लूप को ग्रहण करने का कारण बनता है, जो इसे फ़ाइल नाम के तर्कों पर कुछ हद तक आकर्षित करता है जैसे कि sed -n या awk: LINE: जबकि (<>) {... # आपका प्रोग्राम यहां चला जाता है}

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.