Abrufen einzelner Bytes aus einer Binärdatei in eine Variable mit bash

8

Ich muss ein ziemlich einfaches Bash-Skript erstellen, um Bytes einzeln aus einer Binärdatei abzurufen, eine serielle Schnittstelle zu senden und dann zu warten, bis ein Byte zurückkommt, bevor ich das nächste sende. Dies ist effektiv für einen EEPROM-Programmierer, bei dem eine Standardlösung nicht funktioniert.

Ich bin meistens nicht in der Lage, die Bytes aus der Datei in eine Variable zu ziehen. Darüber hinaus weiß ich, dass ich die Werte über die serielle Schnittstelle wiedergeben und mit dd zurücklesen kann. (Gibt es auch einen besseren Weg, das zu tun?)

Danke für die Hilfe!

Minifigur 666
quelle
Wird das Rückkehrbyte definitiv immer einzeln ankommen? Erhalten Sie wie in nur ein Byte pro gesendetem Byte?
Mikeserv
Sie können mit Hex-Werten arbeiten, z. B. mit dem Befehl xxd -psund xxd -r -ps. Es wäre sicherer.
LatinSuD
IIRC-Bash kann nicht mit Null-Bytes umgehen, daher ist es ungeeignet, mit Binärdaten umzugehen. Die meisten Shells sind nicht für den Umgang mit Binärdaten ausgelegt (zsh ist eine Ausnahme). Ist das ein eingebettetes System? Wenn ja, wie eingeschränkt ist Ihr Werkzeugsatz?
Gilles 'SO - hör auf böse zu sein'
Ja, das Gerät am anderen Ende sollte dasselbe Byte weiterleiten, das gesendet wird, nachdem es am Geräteende einige Verarbeitungsschritte durchlaufen hat.
Minifig666
Dieses Skript läuft nur auf einem Lubuntu-Laptop, daher habe ich keine wirklichen Einschränkungen hinsichtlich der Tools, die ich verwenden kann. Es wäre schön, gerade Binärwerte über die serielle Schnittstelle zu senden, aber ich könnte möglicherweise zwei ASCII-Hex-Zeichen pro Byte senden und dann den Code auf der Geräteseite bearbeiten, um damit fertig zu werden.
Minifig666

Antworten:

7

Verwenden Sie ddoder xxd(Teil von Vim), um beispielsweise ein Byte ( -l) mit dem Offset 100 ( -s) aus der Binärdatei zu lesen. Versuchen Sie:

xxd -p -l1 -s 100 file.bin

Um Hex-Offsets zu verwenden, können Sie in Bash diese Syntax verwenden $((16#64)), z.

echo $((16#$(xxd -p -l1 -s $((16#FC)) file.bin)))

Hiermit wird das Byte im Offset gelesen FCund im Dezimalformat gedruckt.

Alternativ verwenden Sie ddwie:

dd if=file.bin seek=$((16#FC)) bs=1 count=5 status=none

Dadurch werden Rohdaten von 5 Bytes mit Hex-Offset ausgegeben FC.

Dann können Sie es einer Variablen zuweisen, es funktioniert jedoch nicht, wenn Ihre Daten NULL-Bytes haben. Daher können Sie sie entweder überspringen ( xxd -a) oder als Problemumgehung im einfachen hexadezimalen Format speichern.

Zum Beispiel:

  1. Lesen Sie 10 Bytes bei Offset 10 in eine Variable ein, die Bytes im Hex-Format enthält:

    hex=$(xxd -p -l 10 -s 10 file.bin)
  2. Dann schreiben Sie sie in eine Datei oder ein Gerät:

    xxd -r -p > out.bin <<<$hex

Hier sind einige nützliche Funktionen in Bash:

set -e

# Read single decimal value at given offset from the file.
read_value() {
  file="$1"
  offset="$2"
  [ -n "$file" ]
  [ -n "$offset" ]
  echo $((16#$(xxd -p -l1 -s $offset "$file")))
}

# Read bytes in hex format at given offset from the file.
read_data() {
  file="$1"
  offset="$2"
  count="$3:-1"
  xxd -p -l $count -s $offset "$file"
}

Beispielnutzung:

read_value file.bin $((16#FC)) # Read 0xFC offset from file.bin.
Kenorb
quelle
2

Ich bin mir nicht sicher, ob ich das Gesamtbild Ihrer Anfrage verstehe, aber ich habe einige Ansätze, die Sie möglicherweise in Betracht ziehen sollten:

Verwenden ddSie diese Option , um Ihre Eingabedatei zu lesen

exec 3 < Eingabedatei
während wahr
tun
    dd bs = 1 count = 1 <& 3> this_byte
    wenn ! [-s this_byte] # Wenn die Größe Null ist, sind wir fertig.
    dann
        brechen
    fi
    Code mit der Datei this_byte
          ︙
erledigt

Verwenden Sie oddiese Option, um Ihre Eingabedatei zu verarbeiten

od -bv Eingabedatei | während gelesen a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 a16
do # Skip $ a0; Es ist die Adresse.
    für byte_val in "$ a1" "$ a2" "$ a3" "$ a4" "$ a5" "$ a6" "$ a7" "$ a8" "$ a9" "$ a10" \
                                "$ a11" "$ a12" "$ a13" "$ a14" "$ a15" "$ a16"
    tun
        if ["$ byte_val" = ""]
        dann
            brechen
        fi
        Code mit dem Wert
         $ byte_val zum Beispiel: echo -e "\ 0 $ byte_val \ c"
              ︙
    erledigt
erledigt
G-Man sagt "Reinstate Monica"
quelle
1

Dies ist wahrscheinlich einfacher mit einem winzigen C-Programm oder einem (fast) einzeiligen Programm in einer Skriptsprache wie Python oder Perl.

Die Unix-Shell ist ein Interpreter für Benutzerbefehle , der (zur Erleichterung des Schreibens sich wiederholender Befehle) rudimentäre Programmiermöglichkeiten bietet. Es wird oft (falsch) verwendet, weil eine Version der Bourne-Shell garantiert auf jedem Unix-System verfügbar ist, nicht weil es eine besonders gute (oder sogar halbwegs anständige) Programmiersprache ist.

vonbrand
quelle
0

Sie können perlstattdessen verwenden:

$ printf '%s%s%s' 'ăâé' | LC_ALL=C.UTF-8 perl -Mopen=locale -ne '
    BEGIN { $/ = \1 }
    printf "%s\n", $_;
'
ă
â
é
cuonglm
quelle
Sie drucken nur das gleiche Byte zurück? So wie ich es verstehe, möchte der Fragesteller wissen, wie man ein Byte von einer Eingabe liest, es zur Ausgabe druckt und dann auf ein anderes Byte von einer anderen Eingabe wartet, bevor er die Schleife fortsetzt. Es sei denn, Sie tun das bereits und ich verstehe es nicht - was durchaus möglich ist.
Mikeserv