बाइनरी फ़ाइल सामग्री को पढ़ने के लिए बैश स्क्रिप्ट का उपयोग कैसे करें?


15

मैं एक चरित्र पढ़ना चाहता हूं और फिर स्ट्रिंग की एक निश्चित लंबाई (स्ट्रिंग को फ़ाइल में शून्य समाप्त नहीं किया जाता है, और इसकी लंबाई पूर्ववर्ती चरित्र द्वारा दी गई है)।

मैं इसे बाश स्क्रिप्ट में कैसे कर सकता हूं? स्ट्रिंग चर को कैसे परिभाषित किया जाए ताकि मैं उस पर कुछ पोस्ट-प्रोसेसिंग कर सकूं?

जवाबों:


19

यदि आप शेल उपयोगिताओं के साथ रहना चाहते हैं, तो आप headकई बाइट्स निकालने के लिए उपयोग कर सकते हैं , और odएक बाइट को संख्या में परिवर्तित करने के लिए।

export LC_ALL=C    # make sure we aren't in a multibyte locale
n=$(head -c 1 | od -An -t u1)
string=$(head -c $n)

हालाँकि, यह बाइनरी डेटा के लिए काम नहीं करता है । दो समस्याएं हैं:

  • कमांड प्रतिस्थापन $(…)स्ट्रिप कमांड आउटपुट में अंतिम न्यूलाइन्स बनाता है। एक काफी आसान वर्कअराउंड है: सुनिश्चित करें कि आउटपुट एक नई रेखा के अलावा एक चरित्र में समाप्त होता है, फिर उस एक चरित्र को पट्टी करें।

    string=$(head -c $n; echo .); string=${string%.}
  • बैश, अधिकांश गोले की तरह, अशक्त बाइट्स से निपटने में खराब है । बैश 4.1 के रूप में, नल बाइट्स केवल कमांड प्रतिस्थापन के परिणाम से गिराए जाते हैं। डैश 0.5.5 और पीडीएक्स 5.2 में समान व्यवहार है, और एटीटी क्श पहले नल बाइट पर पढ़ना बंद कर देता है। सामान्य तौर पर, गोले और उनकी उपयोगिताओं को द्विआधारी फ़ाइलों से निपटने के लिए तैयार नहीं किया जाता है। (Zsh अपवाद है, यह अशक्त बाइट्स का समर्थन करने के लिए डिज़ाइन किया गया है।)

यदि आपके पास बाइनरी डेटा है, तो आप पर्ल या पायथन जैसी भाषा में स्विच करना चाहेंगे।

<input_file perl -e '
  read STDIN, $c, 1 or die $!;    # read length byte
  $n = read STDIN, $s, ord($c);   # read data
  die $! if !defined $n;
  die "Input file too short" if ($n != ord($c));
  # Process $s here
'
<input_file python -c '
  import sys
  n = ord(sys.stdin.read(1))      # read length byte
  s = sys.stdin.read(n)           # read data
  if len(s) < n: raise ValueError("input file too short")
  # Process s here
'

+1 शेल स्क्रिप्ट हमेशा उपयुक्त नहीं होती हैं
फ़ोर्सफ़्सक

2
exec 3<binary.file     # open the file for reading on file descriptor 3
IFS=                   #
read -N1 -u3 char      # read 1 character into variable "char"

# to obtain the ordinal value of the char "char"
num=$(printf %s "$char" | od -An -vtu1 | sed 's/^[[:space:]]*//')

read -N$num -u3 str    # read "num" chars
exec 3<&-              # close fd 3

5
read -Nअशक्त बाइट्स पर रुक जाता है, इसलिए यह बाइनरी डेटा के साथ काम करने का एक उपयुक्त तरीका नहीं है। सामान्य तौर पर, zsh के अलावा अन्य गोले नल के साथ सामना नहीं कर सकते।
गिलेस एसओ- बुराई को रोकना '

2

यदि आप शेल में बाइनरी फ़ाइल से निपटने में सक्षम होना चाहते हैं, तो सबसे अच्छा विकल्प (केवल?) हेक्सडंप टूल के साथ काम करना है ।

hexdump -v -e '/1 "%u\n"' binary.file | while read c; do
  echo $c
done

केवल एक्स बाइट्स पढ़ें:

head -cX binary.file | hexdump -v -e '/1 "%u\n"' | while read c; do
  echo $c
done

लंबाई पढ़ें (और लंबाई के रूप में 0 के साथ काम करें) और फिर बाइट दशमलव मान के रूप में "स्ट्रिंग":

len=$(head -c1 binary.file | hexdump -v -e '/1 "%u\n"')
if [ $len -gt 0 ]; then
  tail -c+2 binary.file | head -c$len | hexdump -v -e '/1 "%u\n"' | while read c; do
    echo $c
  done
fi

आदेशों का एक समूह प्रस्तुत करने के बजाय, क्या आप बता सकते हैं कि वे क्या करते हैं और कैसे काम करते हैं? विकल्पों का क्या मतलब है? उपयोगकर्ता आपके आदेशों से क्या आउटपुट की उम्मीद कर सकता है? कृपया टिप्पणियों में प्रतिक्रिया न दें;  इसे स्पष्ट और अधिक पूर्ण बनाने के लिए अपना उत्तर संपादित करें।
जी-मैन का कहना है कि 'मोनिका'

2
ठीक है, मैं यहाँ मैनपेज़ कॉपी कर सकता हूँ, लेकिन मुझे यह बात दिखाई नहीं देती। यहां केवल मूल कमांड का उपयोग किया जाता है, एकमात्र चाल हेक्सडंप का उपयोग है।
क्लेमेंट मौलिन - सिंपल रोजो

2
डाउनवोटिंग क्योंकि आपके पसंद नहीं / मेरे जवाब को गंभीरता से समझते हैं?
क्लेमेंट मौलिन - सिंपल रेज़ो

1

अद्यतन (अड़चन के साथ): ... यह प्रश्न / उत्तर (मेरा उत्तर) मुझे उस कुत्ते के बारे में सोचता है जो कार का पीछा करता रहता है .. एक दिन, आखिरकार, वह कार को पकड़ता है .. ठीक है, उसने इसे पकड़ लिया, लेकिन वह वास्तव में इसके साथ ज्यादा कुछ नहीं कर सकता है ... यह ऐक्सर 'स्ट्रिंग्स' को पकड़ता है, लेकिन फिर आप उनके साथ ज्यादा कुछ नहीं कर सकते, अगर उनके पास अशक्त बाइट्स हैं ... (तो एक बड़ा +1 to Gilles जवाब .. एक अन्य भाषा यहाँ क्रम में हो सकती है।)

ddकिसी भी और सभी डेटा को पढ़ता है ... यह निश्चित रूप से शून्य पर "लंबाई" के रूप में बाऊल नहीं करेगा ... लेकिन अगर आपके डेटा में कहीं भी \ x00 है, तो आपको रचनात्मक होना होगा कि आप इसे कैसे संभालते हैं; ddइसके साथ कोई प्रस्ताव नहीं है, लेकिन आपकी शेल स्क्रिप्ट में समस्याएं होंगी (लेकिन यह इस बात पर निर्भर करता है कि आप डेटा के साथ क्या करना चाहते हैं) ... निम्नलिखित मूल रूप से प्रत्येक "डेटा स्ट्रिंग" को आउटपुट करता है, प्रत्येक स्ट्रिन के बीच एक लाइन विभक्त के साथ एक फ़ाइल में। ...

btw: आप कहते हैं "चरित्र", और मुझे लगता है कि आप "बाइट" का अर्थ है ...
लेकिन शब्द "चरित्र" UNICODE के इन दिनों में अस्पष्ट हो गया है, जहां केवल 7-बिट ASCII वर्ण-सेट एक एकल बाइट का उपयोग करता है ... और यहां तक ​​कि यूनिकोड प्रणाली के भीतर, बाइट काउंट्स एन्कोडिंग वर्णों की विधि के आधार पर भिन्न होते हैं , उदा। UTF-8, UTF-16, आदि।

पाठ "वर्ण" और बाइट के बीच अंतर को उजागर करने के लिए यहां एक सरल स्क्रिप्ट है।

STRING="௵"  
echo "CHAR count is: ${#STRING}"  
echo "BYTE count is: $(echo -n $STRING|wc -c)" 
# CHAR count is: 1
# BYTE count is: 3  # UTF-8 ecnoded (on my system)

यदि आपका लंबाई वर्ण 1-बाइट लंबा है और एक बाइट-लंबाई इंगित करता है , तो इस स्क्रिप्ट को ट्रिक करना चाहिए, भले ही डेटा में यूनिकोड वर्ण हों ... ddकेवल किसी भी स्थानीय सेटिंग की परवाह किए बिना बाइट्स देखता है ...

यह स्क्रिप्ट ddबाइनरी फ़ाइल को पढ़ने के लिए उपयोग करती है और "====" विभक्त द्वारा अलग किए गए तार को आउटपुट करती है ... परीक्षण डेटा के लिए अगली स्क्रिप्ट देखें

#   
div="================================="; echo $div
((skip=0)) # read bytes at this offset
while ( true ) ; do
  # Get the "length" byte
  ((count=1)) # count of bytes to read
  dd if=binfile bs=1 skip=$skip count=$count of=datalen 2>/dev/null
  (( $(<datalen wc -c) != count )) && { echo "INFO: End-Of-File" ; break ; }
  strlen=$((0x$(<datalen xxd -ps)))  # xxd is shipped as part of the 'vim-common' package
  #
  # Get the string
  ((count=strlen)) # count of bytes to read
  ((skip+=1))      # read bytes from and including this offset
  dd if=binfile bs=1 skip=$skip count=$count of=dataline 2>/dev/null
  ddgetct=$(<dataline wc -c)
  (( ddgetct != count )) && { echo "ERROR: Line data length ($ddgetct) is not as expected ($count) at offset ($skip)." ; break ; }
  echo -e "\n$div" >>dataline # add a newline for TEST PURPOSES ONLY...
  cat dataline
  #
  ((skip=skip+count))  # read bytes from and including this offset
done
#   
echo

बाहर जाएं

यह स्क्रिप्ट परीक्षण डेटा बनाता है जिसमें प्रति पंक्ति 3-बाइट उपसर्ग शामिल होता है ...
उपसर्ग एक एकल UTF-8 एन्कोडेड यूनिकोड वर्ण है ...

# build test data
# ===============
  prefix="௵"   # prefix all non-zero length strings will this obvious 3-byte marker.
  prelen=$(echo -n $prefix|wc -c)
  printf \\0 > binfile  # force 1st string to be zero-length (to check zero-length logic) 
  ( lmax=3 # line max ... the last on is set to  255-length (to check  max-length logic)
    for ((i=1;i<=$lmax;i++)) ; do    # add prefixed random length lines 
      suflen=$(numrandom /0..$((255-prelen))/)  # random length string (min of 3 bytes)
      ((i==lmax)) && ((suflen=255-prelen))      # make last line full length (255) 
      strlen=$((prelen+suflen))
      printf \\$((($strlen/64)*100+$strlen%64/8*10+$strlen%8))"$prefix"
      for ((j=0;j<suflen;j++)) ; do
        byteval=$(numrandom /9,10,32..126/)  # output only printabls ASCII characters
        printf \\$((($byteval/64)*100+$byteval%64/8*10+$byteval%8))
      done
        # 'numrandom' is from package 'num-utils"
    done
  ) >>binfile
#

1
आपका कोड जितना जटिल होना चाहिए, उससे कहीं अधिक जटिल है, विशेष रूप से यादृच्छिक परीक्षण डेटा जनरेटर। आप /dev/urandomअधिकांश यूनियनों से यादृच्छिक बाइट्स प्राप्त कर सकते हैं । और यादृच्छिक परीक्षण डेटा सबसे अच्छा परीक्षण डेटा नहीं है, आपको बाध्य मामलों में कठिन मामलों जैसे, यहाँ, अशक्त वर्णों और न्यूलाइन को संबोधित करना सुनिश्चित करना चाहिए।
गाइल्स 'एसओ- बुराई को रोकना'

हाँ धन्यवाद। मैं / dev / random का उपयोग करने के बारे में सोचता था, लेकिन पता लगा कि टेस्ट डेटा जीन का कोई बड़ा आयात नहीं था, और मैं ड्राइव 'numrandom' का परीक्षण करना चाहता था (जिसे आपने कहीं और उल्लेख किया है; 'num-utils's anome nice features।)। मैंने अभी आपके उत्तर पर बारीकी से विचार किया है, और महसूस किया कि आप बहुत ही समान काम कर रहे हैं, सिवाय इसके कि यह अधिक रसीला है :) .. मैंने नोटिस नहीं किया था कि आपने मुख्य बिंदुओं को 3 लाइनों में बताया था! मैंने आपके अन्य भाषा- संदर्भों पर ध्यान केंद्रित किया था .. इसे काम पर लाना एक अच्छा अनुभव था, और मैं अब अन्य भाषाओं के आपके संदर्भों को बेहतर ढंग से समझता हूँ! \ x00 एक शेल-स्टॉपर हो सकता है
पीटर।

0

यह केवल एक बाइनरी फ़ाइल की प्रतिलिपि बनाता है:

 while read -n 1 byte ; do printf "%b" "$byte" ; done < "$input" > "$output"
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.