Trong bash, làm thế nào để chuyển đổi 8 byte thành int không dấu (64 bit LE)?


8

Làm cách nào tôi có thể 'đọc / giải thích' 8 byte dưới dạng int không dấu (Little Endian) ?
Có lẽ có một chuyển đổi ma thuật Bash-fu cho điều này?

CẬP NHẬT:
Có vẻ như một cái gì đó có dây chéo trong việc giải thích câu hỏi của tôi. Đây là một ví dụ rộng hơn về những gì tôi đang cố gắng làm.

Tôi muốn đọc 64k đầu tiên (và cuối cùng) của một tệp. Mỗi từ 8 byte sẽ được hiểu là một số nguyên không dấu Little-Endian 64 bit. Các số nguyên này sẽ được sử dụng trong tính toán băm xác định duy nhất tệp. Vì vậy, có rất nhiều tính toán để thực hiện, ∴ tốc độ được ưa thích, nhưng không quan trọng. (Tại sao tôi lại làm việc đó? Bởi vì smplayerbăm tên các file của mình chơi phương tiện truyền thông .ini, và tôi muốn truy cập và sửa đổi những tập tin này, vì vậy tôi đang bắt chước C ++ của SMPlayer trong Bash.)

Một giải pháp phục vụ cho việc chấp nhận đầu vào có đường ống sẽ là tối ưu và có lẽ là điều cần thiết vì cách các biến Bash không thể xử lý \ x00 ..

Tôi nhận ra rằng một cái gì đó như thế này có lẽ phù hợp hơn với những thứ như Python, Perl và C / C ++, nhưng tôi không biết Python và Perl, và mặc dù tôi có thể làm điều đó trong C ++, đã nhiều năm kể từ khi tôi sử dụng nó và tôi đang cố tập trung vào Bash.

Đoạn ngắn Perl và Python là tốt. Bash được ưa thích (nhưng không phải hy sinh tốc độ).


1
Làm thế nào để các byte trông như thế nào? 4 125 -19 0 có thể được xem là 4 byte, "\ t.-X" có thể được xem là 4 byte (tôi chọn 4 vì nó ngắn hơn), nhưng còn về 319 và "ö §« ¢ "thì sao? Hoặc là 8 byte đầu tiên trong một tệp tùy ý?
người dùng không biết

'4 125 -19 0' không phải là 4 byte. Nó có thể là biểu diễn số nguyên thập phân của 4 byte đang được diễn giải trên giá trị số nguyên nhị phân của nó, nhưng nó sử dụng 8 byte (không bao gồm khoảng trắng) .. '\ t.-X' là sự hối tiếc của 4 byte trong ASCII / 'C ' Phong cách; nó đang sử dụng 5 byte .. ö§«¢không phải là byte; chúng là các ký tự Unicode trên danh nghĩa, sử dụng 8 byte khi được mã hóa bằng UTF-8 và UTF-32 và 16 byte khi được mã hóa bằng UTF-32 .. Như thường gặp trong các trang man, tôi đang đề cập đến các octet 8 bit; chỉ là một Byte 8 bit thông thường đơn giản .. Tôi muốn diễn giải 8 trong số đó là một Big-Endian int 64 bit không dấu
Peter.O

Không, '4 125 -19 0' là đại diện hợp lệ, có thể hoặc 4 byte. Các pixel trên màn hình sử dụng nhiều hơn 8 byte. "ö §« ¢ "tất nhiên là byte, vì mọi thông tin kỹ thuật số có thể được biểu thị bằng byte - giống như mọi khoảng cách có thể được biểu thị bằng mét và inch. Câu hỏi của tôi là, làm thế nào để bạn có được các byte, chúng được thể hiện như thế nào? Ví dụ, một octet 8 bit sẽ là 00101101.
người dùng không xác định

Các byte đầu vào của tôi không "đại diện" .. Tôi đang nói về một byte là một byte ... không hơn, không kém ... tức là. dữ liệu thô ... Sau đó, tôi muốn giải thích 8 trong số các byte thô đó dưới dạng dài không dấu, nghĩa là. một số nguyên không dấu 64 bit ... Tôi sẽ không thể hiện trực quan các số nguyên này .. Tôi sẽ sử dụng chúng trong phép tính băm. ... Tôi quan tâm đến các giá trị nhị phân của mỗi byte ... Hãy nghĩ về nó, đó là "biểu diễn" ... Tôi muốn làm việc với giá trị cơ bản 2 của 8 bit, tức là. Tôi muốn không xử lý byte theo bất kỳ cách đặc biệt nào.
Peter.O

Một byte là một byte và một bông hồng là một bông hồng, nhưng bash không có hệ thống loại, phải không? Chà - Tôi biết chuỗi và số nguyên và booleans trong bash, nhưng không có loại byte, vì vậy tôi phải lấy một thứ khác - chuỗi, số, booleans, mảng, tệp ... Bạn có thể chỉ ra cách bạn có được các byte này trong kịch bản của bạn? Hoặc chúng được mã hóa trực tiếp (thô?) Trong kịch bản?
người dùng không xác định

Câu trả lời:


6

Bash là công cụ sai hoàn toàn. Vỏ rất tốt trong việc dán các mảnh và mảnh lại với nhau; xử lý văn bản và số học được cung cấp ở bên cạnh, và xử lý dữ liệu hoàn toàn không nằm trong tầm nhìn của họ.

Tôi sẽ tìm Python trên Perl, bởi vì Python có bignums ngay lập tức. Sử dụng struct.unpackđể giải nén dữ liệu.

#!/usr/bin/env python
import os, struct, sys
fmt = "<" + "Q" * 8192
header_bytes = sys.stdin.read(65536)
header_ints = list(struct.unpack(fmt, header_bytes))
sys.stdin.seek(-65536, 2)
footer_bytes = sys.stdin.read(65536)
footer_ints = list(struct.unpack(fmt, header_bytes))
# your calculations here

Đây là câu trả lời của tôi cho câu hỏi ban đầu. Câu hỏi sửa đổi không liên quan nhiều đến bản gốc, đó là về việc chuyển đổi một chuỗi 8 byte thành số nguyên 64 bit mà nó đại diện theo thứ tự cuối nhỏ.

Tôi không nghĩ bash có bất kỳ tính năng tích hợp nào cho việc này. Đoạn mã sau đặt athành một chuỗi là biểu diễn thập lục phân của số tương ứng với các byte trong chuỗi đã chỉ định theo thứ tự cuối lớn .

a=0x$(printf "%s" "$string" |
      od -t x1 -An |
      tr -dc '[:alnum:]')

Đối với thứ tự endian nhỏ, đảo ngược thứ tự của các byte trong chuỗi gốc. Trong bash, và với một chuỗi có độ dài đã biết, bạn có thể làm

a=0x$(printf "%s" "${string:7:1}${string:6:1}${string:5:1}${string:4:1}${string:3:1}${string:2:1}${string:1:1}${string:0:1}" |
      od -t x1 -An |
      tr -dc '[:alnum:]')

Bạn cũng có thể nhận được tính xác thực ưa thích của nền tảng nếu odhỗ trợ các loại 8 byte.

a=0x$(printf "%s" "$string" |
      od -t x8 -An |
      tr -dc '[:alnum:]')

Việc bạn có thể thực hiện số học hay không $asẽ tùy thuộc vào việc bash của bạn có hỗ trợ số học 8 byte hay không. Ngay cả nếu có, nó sẽ coi nó như một giá trị đã ký.

Ngoài ra, sử dụng Perl:

a=0x$(perl -e 'print unpack "Q<", $ARGV[0]' "$string")

Nếu perl của bạn được biên dịch mà không có hỗ trợ số nguyên 64 bit, bạn sẽ cần phải chia nhỏ các byte.

a=0x$(perl -e 'printf "%x%08x\n", reverse unpack "L<L<", $ARGV[0]' "$string")

(Thay thế <bằng >endian lớn hoặc loại bỏ nó để có được endianness nền tảng.)


@Gille; ... Tôi vẫn đang xem xét các tùy chọn của bạn (theo tốc độ; tôi đang xử lý 64k), nhưng một lưu ý phụ, tôi khá chắc chắn rằng Little-Endian chỉ có byte thứ tự thấp nhất được chuyển lên cao -order postion ... Nó không phải là một sự đảo ngược hoàn toàn cho kích thước từ lớn hơn ... nó là một sự đảo ngược hoàn toàn khi các từ có 16 bit :)
Peter.O

@fred: Ah, bạn không nói bạn muốn tốc độ. Perl sẽ chiến thắng tất cả trừ khi bạn xoa bóp mà đầu ra od tốt hơn. Phiên bản nhỏ nào không hoạt động? Tôi nhận được đầu ra mà tôi mong đợi.
Gilles 'SO- ngừng trở nên xấu xa'

@Gilles: ôi! xin lỗi ... Bạn nói đúng về thứ tự byte; nó hoàn toàn đảo ngược ... (tôi đã biết (?) trong một thời gian dài, rằng đó chỉ là byte thứ tự thấp và đó là điều tốt mà tôi chưa bao giờ cần sử dụng kiến ​​thức của mình trước ngày hôm nay :) .. .. re perl , tôi nhận được lỗi này trong khi sử dụng "12345678" làm chuỗi ..
Peter.O

@fred: Lỗi gì? Lệnh nào bạn đã chạy, đầu ra là gì, bạn mong đợi nó là gì?
Gilles 'SO- ngừng trở nên xấu xa'

@Gilles: Thông báo lỗi: Invalid type 'Q' in unpack at -e line 1.... Lệnh (sử dụng chuỗi 8 byte ngẫu nhiên ): perl -e 'print unpack "Q<", $ARGV[0]' "12345678"... Đầu ra: chỉ thông báo lỗi ... perl vereion: v5.10.1
Peter.O

4

Phương pháp python của Gilles chắc chắn là nhanh hơn, nhưng tôi nghĩ rằng tôi chỉ cần ném vào * bash *** + *** std-single-aim-tools * này như là một công cụ chuyên nghiệp cho nhà máy .. Có lẽ nhiều về 'bc ' như mọi thứ khác ... Nó có rất nhiều công cụ Khởi tạo, để phục vụ cho các tệp đầu vào có giá trị dưới 64k ... Hàm băm được khởi tạo theo chiều dài của tệp và sau đó từng số nguyên 64 bit được thêm vào nó; gây ra tràn số nguyên (dự kiến) .. bcđược quản lý để thực hiện thủ thuật ...

# This script reads 8196 8-byte blocks (64 KiB) from the head and tail of a file
# Each 8-bytes block is interpreted as an unsigned 64-bit Little-Endian integer.
# The head integers and tail integers ar printed to stdout; one integer per line.
#
# INIT: If the file is smaller than 64k, calculate the number of unsigned ints to read 
# ====
  file="$1"
  flen=($(du -b "$file"))           # file length
  qlen=8                            # ui64 length in bytes
    ((flen<qlen)) && exit 1         # file is too short -- exit 
  bmax=$((64*1024))                 # byte end of read (== byte max to read)
    ((flen<bmax)) && ((bmax=flen))  # reduce byte max to file length
  qmax=$((bmax/qlen))               # ui64 end of read (== ui64 max to read)
    (((qmax*qlen)<bmax)) && ((bmax=(qmax*qlen))) # round down byte max (/8)
  hash=$(echo $flen |xxd -p -u)
# 
# MAIN
# ====
  for skip in 0  $((flen-bmax)) ;do
    hash=$(dd if="$file" bs=1 count=$bmax skip=$skip 2>/dev/null |
             xxd -p -u -c 8 |
             { echo -e " ibase=16 \n obase=10 \n scale=0 \n hash=$hash \n ouint=10000000000000000 "; \
               sed -re "s/(..)(..)(..)(..)(..)(..)(..)(..)/hash=(hash+\8\7\6\5\4\3\2\1)%ouint/"; \
               echo "hash"; } |bc)
  done
  echo $hash
#

# Output:
16A6528E803325FF

0

Điều này chấp nhận stdin và in 64kB cuối cùng của tệp dưới dạng số nguyên thập lục phân 8 byte không dấu trong phần cuối của máy (endian trên x86). Để in 64kB đầu tiên, thay thế 'đuôi' bằng 'đầu'

tail -c $(( 1024*64 )) | xxd -ps |tr -d '\n' | while read -N16 i ; do echo 0x$i ; done

GIỚI HẠN: cố gắng chuyển đổi đầu ra thành số thập phân bằng printf sẽ dẫn đến các lỗi ngoài phạm vi

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.