বাইনারি ফাইল সামগ্রী পড়তে বাশ স্ক্রিপ্টটি কীভাবে ব্যবহার করবেন?


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.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 .. অন্য ভাষা এখানে হতে পারে।)

ddযে কোনও এবং সমস্ত ডেটা পড়ে ... এটি অবশ্যই "দৈর্ঘ্য" হিসাবে শূন্যের দিকে ঝাঁকিয়ে উঠবে না ... তবে আপনার ডেটাতে কোথাও যদি 00 x00 থাকে, আপনি কীভাবে এটি পরিচালনা করবেন আপনার সৃজনশীল হতে হবে; ddএটির সাথে কোনও সমস্যা নেই, তবে আপনার শেল স্ক্রিপ্টটিতে সমস্যা হবে (তবে এটি আপনি ডেটা দিয়ে কী করতে চান তার উপর নির্ভর করে) ... নিম্নলিখিতটি প্রতিটি স্ট্রিনের মধ্যে একটি লাইন বিভাজক সহ একটি ফাইলের সাথে প্রতিটি "ডেটা স্ট্রিং" আউটপুট দেয় following ...

বিটিডব্লিউ: আপনি "চরিত্র" বলছেন এবং আমি ধরে নিয়েছি আপনি "বাইট" বলতে চাইছেন ...
তবে ইউনিকোডির এই দিনগুলিতে "চরিত্র" শব্দটি দ্ব্যর্থক হয়ে উঠেছে, যেখানে কেবল--বিট এএসসিআইআই চরিত্র-সেট অক্ষর অনুসারে একক বাইট ব্যবহার করে ... এমনকি ইউনিকোড সিস্টেমের মধ্যেও, এনকোডিং অক্ষরগুলির পদ্ধতির উপর নির্ভর করে বাইট গণনাগুলি পৃথক হয়ে থাকে , যেমন। ইউটিএফ -8, ইউটিএফ -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বেশিরভাগ ইউনিট থেকে এলোমেলো বাইট পেতে পারেন । এবং এলোমেলো পরীক্ষার ডেটা সেরা পরীক্ষার ডেটা নয়, আপনার এখানে জটিল ক্ষেত্রে যেমন নাল অক্ষর এবং সীমানা স্থানে নিউলাইনটি চিহ্নিত করা উচিত তা নিশ্চিত করা উচিত।
গিলস 16'80 এ

হ্যা ধন্যবাদ. আমি / dev / এলোমেলো ব্যবহারের কথা ভেবেছিলাম কিন্তু পরীক্ষার ডেটা জেনটি কোনও দুর্দান্ত আমদানির বিষয় নয়, এবং আমি ড্রাইভিং 'নামানড্রোম' (যা আপনি অন্য কোথাও উল্লেখ করেছেন; 'নাম-ব্যবহারের সুন্দর বৈশিষ্ট্যগুলি) পরীক্ষা করতে চেয়েছিলেন) আমি আপনার উত্তরটি খুব ঘনিষ্ঠভাবে দেখেছি, এবং বুঝতে পেরেছি যে আপনি আরও একই জিনিস করছেন, এটি ব্যতিরেকে ছাড়াও :) .. আমি খেয়াল করি নি যে আপনি 3 পংক্তিতে মূল পয়েন্টগুলি বর্ণনা করেছেন! আমি আপনার অন্যান্য ভাষার উল্লেখগুলিতে মনোনিবেশ করেছি .. এটির কাজ করা একটি ভাল অভিজ্ঞতা ছিল এবং আমি এখন অন্য ভাষাগুলির জন্য আপনার উল্লেখগুলি আরও ভালভাবে বুঝতে পারি!
00

0

এটি কেবল একটি বাইনারি ফাইল অনুলিপি করুন:

 while read -n 1 byte ; do printf "%b" "$byte" ; done < "$input" > "$output"
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.