Bashで8バイトを符号なし整数(64ビットLE)に変換するには?

Bashで8バイトを符号なし整数(64ビットLE)に変換するには?

8バイトをどのように「読み取り/解釈」するのですか?署名されていない整数(リトルエンディアン)
たぶんこれを達成するためのBash-fuの魔法の変換はありますか?

更新:
私の質問の説明に何かが交差しているようです。以下は、私がやろうとしている作業のより広範な例です。

ファイルの最初(および最後)64kを読みたいです。各8バイトの単語は、64ビットのLittle-Endian符号なし整数として解釈されます。これら整数ファイルを一意に識別するために使用されるハッシュ計算。したがって、多くの計算が必要であるため、∴速度が好ましいが、重要ではない。 (私がこれを行う理由は何ですか?smplayer再生されるメディアの.iniファイルの名前をハッシュし、そのファイルにアクセスして変更しようとしているので、BashでsmplayerのC ++コードを模倣します。)

パイプ入力を受け入れるのに適した解決策が最適であり、Bash変数が\ x00.を処理できないため、これは不可欠です。

私はこのようなものがPython、Perl、C / C ++などの言語に適している可能性があることを知っていますが、PythonとPerlを知らず、C ++でこれを行うことはできますが、実際には使用しませんでした。長年、私はFocus on Bashを試してきました。

短いPerlとPythonのスニペットをお勧めします。 Bashが優先されます(ただし速度が低下するわけではありません)。

答え1

Bashは単に間違ったツールです。シェルはピースを互いに貼り付けるのに堪能です。テキスト処理と算術は側面で提供されますが、データ処理は単にその権限に属しません。

Pythonにはすぐに大きな数字があるので、Perlの代わりにPythonを選択します。使用struct.unpackデータを解凍します。

#!/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

これが元の質問に対する私の答えです。修正された問題は、8バイトシーケンスをリトルエンディアンシーケンスの64ビット整数表現に変換する元の問題とはほとんど関係がありません。

私はbashに組み込まれた機能がないと思います。次のコードスニペットは、a指定された文字列のバイトに対応する数値の16進表現である文字列を設定します。ビッグエンディアン方式注文する。

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

リトルエンディアン順序の場合は、元の文字列のバイト順序を逆に置き換えます。 Bashでは、既知の長さの文字列に対して次のことができます。

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:]')

od8バイトタイプをサポートしている場合は、プラットフォームで好みのエンディアンを入手することもできます。

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

算術を実行する能力は、$abashが8バイトの算術をサポートしているかどうかによって異なります。それでも、それを符号付き値として扱います。

またはPerlを使用して:

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

Perlが64ビット整数サポートなしでコンパイルされている場合は、バイトを分割する必要があります。

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

(ビッグエンディアン<に交換するか、プラットフォームエンディアンのために削除してください。)>

答え2

GillesのPythonアプローチは確かに高速ですが、一般的なツールとして*bash***+***std-single-Purpose-tools*に投げ込むようです。おそらくそれほどでしょう。「BC」について他のものと同様に、64k未満の入力ファイルを処理するための多くの初期化項目があります。ハッシュファイルの長さに初期化され、次に各64ビット整数を順に追加して(予想される)整数オーバーフローを発生させます。bcこれを実行しました。

# 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

答え3

これはstdinを受け入れ、ファイルの最後の64kBをシステムのエンディアン(x86ではリトルエンディアン)の符号なし8バイトの16進整数として印刷します。最初の64kBを印刷するには、「テール」を「ヘッド」に置き換えます。

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

制限事項:printfを使用して出力を10進数に変換しようとすると、範囲外のエラーが発生します。

関連情報