1
votes

Supprimer 0xff à la fin d'une image flash binaire à l'aide du script bash

J'ai un outil qui génère une image binaire de 32 Mo à écrire dans une mémoire flash. Mais seuls les 2 premiers Mo contiennent des données précieuses, le reste n'est que 0xff. Je voudrais donc supprimer les octets 0xff de la fin du fichier en utilisant un joli script bash / makefile. Je pourrais utiliser head:

head -c 2M test.bin > out.bin

Mais je ne connais pas la longueur réelle, donc je voudrais trouver la première occurrence de 0xff à partir de la fin du fichier comme entrée à head ou similaire.

J'exécute mon outil à partir d'un Makefile sur cygwin, donc ce serait bien s'il pouvait être exécuté en utilisant les outils standard bash / makefile.


3 commentaires

Peut-il y avoir un 0xff dans vos données valides, ou êtes-vous sûr qu'il n'est présent que dans les derniers ~ 30 Mo?


quel est le problème d'utiliser dd et de ne copier que les 2 premiers Mo de données?


Le fichier peut être compris entre 2 Mo et 4 Mo. 4 Mo prend beaucoup plus de temps à programmer dans mon appareil relativement lent.Il peut contenir 0xff au début et au milieu qui ne devraient pas être touchés.


3 Réponses :


1
votes

Ce sed one liner ne fonctionnera que sur la dernière ligne du fichier et supprimera tous les octets 0xFF de fin.

sed '$ s/\xff*$//' file > file.out


18 commentaires

L'utilisation de sed, qui est un éditeur de texte, sur des fichiers binaires n'est pas si simple. Ce que vous suggérez ne fera pas ce qui est demandé. Par exemple, il supprimera une série d'octets 0xff s'il est suivi d'une fin de ligne, même s'ils ne sont pas à la fin du fichier. L'utilisation de ceci endommagera très probablement certains fichiers binaires ...


J'ai peur que vous vous trompiez. Cela fera exactement ce qui a été demandé pour n'importe quel fichier. Il supprimera uniquement les octets 0xff de fin indépendamment des autres contenus bruts du fichier. J'ai testé cela explicitement et je mets à jour ma réponse avec ces tests montrant que cela fonctionne comme prévu.


Cela fonctionnait parfaitement pour les 100 premiers octets, mais après cela, il a commencé à supprimer les occurrences uniques de 0xff et à modifier l'ordre des octets! Essayer de comprendre ce que fait vraiment le script sed, mais peut-être pouvez-vous expliquer. Après hexdump, j'obtiens le diff suivant: Avant: 009dd00 0000 f840 45b2 ff2f 9f0a 0e47 0000 0000 Après: 009dd00 0000 f840 45b2 0a2f 479f 000e 0000 0000


Hmm, eh bien je suppose que mon test que je viens d'ajouter à ma solution n'est pas aussi robuste que je le pensais. Je vais essayer ceci sur un fichier beaucoup plus volumineux ...


J'ai essayé cela sur un fichier beaucoup plus volumineux et j'ai confirmé que cela fonctionne. J'ai pris le fichier que j'ai mentionné dans mon exemple et je l'ai simplement répété encore et encore jusqu'à ce que j'aie un fichier de 285570 octets avec des octets 0xff à la fin. Ensuite, j'ai supprimé manuellement les octets de fin et pris la somme md5. Enfin, j'ai utilisé mon script sed pour supprimer les octets et utilisé la somme md5 pour confirmer que le fichier était identique. Êtes-vous certain d'avoir correctement écrit la commande sed?


BTW j'utilise GNU sed 4.7


Vous devriez essayer le contre-exemple que j'ai mentionné: un court fichier avec 0xff suivi d'une nouvelle ligne, suivi de toute autre chose.


Tu as raison. Mon fichier de test n'avait pas de 0xff suivi d'une nouvelle ligne. Ma réponse ne fonctionne pas.


@Renauld Pacalet, veuillez consulter ma réponse mise à jour et laissez-moi savoir si vous pouvez voir des problèmes avec elle. Merci pour vos réponses.


Le problème avec cette solution est qu'elle supprimera de gros segments de 0xff au début et au milieu du fichier. Mon original contient 128 octets avec 0xff au début qui est tronqué à 8 octets de 0xff.


@Bimme: absolument, xxd génère plusieurs lignes et l'utilisation de sed traite toujours ces lignes comme indépendantes. Ainsi, les lignes pleines de ff seront supprimées même si ce n'est pas à la fin du flux ...


@AKstat: la chose la plus importante à comprendre avec sed , dans ce cas, est que le signe $ dans votre expression régulière signifie fin de ligne < / b>, pas end-of-file . Donc, à moins que vous n'ayez une seule ligne d'entrée, ce type de solution ne fonctionne pas. Et xxd génère plusieurs lignes sur des fichiers volumineux (ne recherchez pas l'option one-line-output , la longueur maximale est de 256 octets).


J'aimais beaucoup cette solution et j'avais espéré qu'une solution sed était disponible. Dommage que cela n'ait pas fonctionné. Merci beaucoup pour vos efforts!


@RenaudPacalet vous avez absolument raison. J'ai oublié comment xxd insère de nouvelles lignes. J'étais debout bien trop tard hier soir quand je travaillais là-dessus. Consultez ma modification pour supprimer ce problème.


@Bimme Je sais que vous avez déjà accepté une réponse, mais si vous avez la chance de la tester, je serais très désireux de savoir si elle est plus rapide que l'autre solution de votre dossier.


@AKstat Votre dernière solution donne un résultat correct, mais il a fallu 2m50s sur mon fichier de 32 Mo. Je crois que la conversion en ascii-hex est un gros problème, mais aussi toutes les étapes.


@Bimme Je suis vraiment surpris par ça. Sur mon ordinateur portable, il a fallu environ 7 secondes pour s'exécuter sur un fichier de 40 Mo que j'ai généré avec beaucoup de 0xFF et de nouvelles lignes. Cependant, j'ai créé une solution beaucoup plus efficace en utilisant uniquement sed qui ne prend qu'une demi-seconde pour moi. Veuillez consulter ma modification et me faire savoir si les performances s'améliorent pour vous.


@RenaudPacalet que pensez-vous de mon one-liner sed révisé?



0
votes
head -c -1 test.bin > out.bin
The -1 strips the last byte.

2 commentaires

head n'est pas un outil pour objet binaire, de plus il demande de supprimer tous les octets pas seulement un


Oups, je n'ai pas remarqué qu'il avait clairement dit de supprimer tous les octets. En dehors de cela, pourquoi head ne peut-il pas être utilisé avec des fichiers qui ne contiennent pas nécessairement de terminateurs de ligne? Je ne vois aucune restriction de ce genre dans la page de manuel head et je conclurais que l'option -c devrait avoir un sens avec chaque type de fichier.



1
votes

Le comptage du nombre d'octets 0xFF à la fin de votre fichier peut être fait avec une combinaison de hexdump (ou xxd , od ...) pour convertir votre fichier binaire en un flux de valeurs hexadécimales ASCII et un processeur de texte comme awk pour faire le comptage. Exemple:

$(OUTDIR)/%: % | $(OUTDIR)
    base64 -w0 $< | sed -E 's#(////)*//8=$$##' | base64 -d > $@

Ensuite, la suppression de ce nombre d'octets à la fin du fichier peut être effectuée en utilisant par exemple dd ou head . Exemple:

base64 -w0 test.bin | sed -E 's#(////)*//8=$##' | base64 -d > results/test.bin

Dans l'ensemble, votre Makefile pourrait ressembler à:

base64 -d

Il y a quelques subtilités de make comme cibles fausses ( toutes ) , variables automatiques ( $ , $ @ ), prérequis pour la commande uniquement ( | $ (OUTDIR) ), créer des échappements d'expansion dans les recettes (les signes $$ ), créer des fonctions ( wildcard , addprefix ), une recette en une ligne qui utilise la continuation de ligne (le \ en fin de ligne) ... Mais rien de très compliqué. p>

MODIFIER : essayez de trouver une solution plus rapide:

Une autre option peut être plus efficace (environ 20 fois plus rapide sur les fichiers de 32 Mo dans ma super-sim ple tests) si l'utilitaire base64 est disponible et que la taille de vos fichiers est exactement de 32 Mo:

  1. base64 -encode sans emballage:

    sed -E 's#(////)*//8=$##'
    
  2. Utilisez sed pour supprimer toutes les chaînes //// de fin, suivies d'un dernier // 8 = à la fin du fichier (voir la base64 RFC 4648 ) pour comprendre pourquoi ces étranges textes de fin:

    base64 -w0
    
  3. base64 -decode:

    OUTDIR  := results
    OLDBINS := $(wildcard *.bin)
    NEWBINS := $(addprefix $(OUTDIR)/,$(OLDBINS))
    
    .PHONY: all
    
    all: $(NEWBINS)
    
    $(OUTDIR)/%: % | $(OUTDIR)
        n=$$(hexdump -v -e '/1 "%02X\n"' $< | \
          awk '/FF/ {n += 1} !/FF/ {n = 0} END {print n}'); \
        head -c -$$n $< > $@
    
    $(OUTDIR):
        mkdir -p $@
    

One-liner:

head -c -$d test.bin > results/test.bin

Notez que cela peut laisser un ou deux caractères 0xFF à la fin du fichier, en fonction de la taille du fichier d'entrée et du nombre de caractères 0xFF de fin. La nouvelle recette de fabrication serait:

hexdump -v -e '/1 "%02X\n"' test.bin | \
  awk '/FF/ {n += 1} !/FF/ {n = 0} END {print n}'


5 commentaires

Cela semble faire ce dont j'ai besoin. Mais comme vous semblez si intéressé, c'est assez lent, prend 25 s sur mon fichier de 32 Mo. La première solution sed exécutée en environ 1s. Mais c'est bien pour moi. Merci beaucoup pour votre aide!


Je ne suis pas surpris que ce soit lent et, honnêtement, personnellement, j'aurais écrit un petit programme C ou python pour faire de même beaucoup plus rapidement. Il est lent à cause de la traduction du binaire en hexadécimal et du comptage octet par octet par awk . Peut-être que quelqu'un viendra avec une solution plus rapide, mais si vous êtes coincé avec des utilitaires shell standard, je ne m'attends pas à des miracles.


Notez que si la vitesse est un problème et que vous avez un ordinateur multicœur, le Makefile que je propose est sûr en parallèle. Exécutez-le comme make -j4 , par exemple, pour une accélération presque 4 fois sur un ordinateur à 4 cœurs. De plus, les dépendances sont correctement exprimées: si un fichier d'entrée ne change pas, le fichier de sortie ne sera pas re-généré aux prochains appels de make.


Votre tentative pour une solution plus rapide a été en fait plus lente, cela a pris 37 secondes sur mon fichier de 32 Mo.


Gee, j'ai 0,4 seconde sur mon ordinateur ... Eh bien, cela vient probablement de différences importantes entre nos ordinateurs (mémoire peut-être). Au fait, que savez-vous avec certitude de vos fichiers d'entrée? Ce n'est pas si clair d'après votre question. Leurs tailles sont-elles fixées à 32 Mo? Savez-vous déjà avec certitude quelle est la taille maximale de la pièce que vous souhaitez conserver? Cela pourrait probablement être utilisé efficacement pour accélérer l'ensemble du processus.