J'essaie ambitieusement de traduire un code c ++ en bash pour une myriade de raisons.
Ce code lit et manipule un type de fichier spécifique à mon sous-champ qui est écrit et structuré complètement en binaire. Ma première tâche liée aux binaires consiste à copier les 988 premiers octets de l'en-tête, exactement tels quels, et à les placer dans un fichier de sortie dans lequel je peux continuer à écrire pendant que je génère le reste des informations.
Je suis à peu près sûr que ma solution actuelle ne fonctionne pas et, en réalité, je n'ai pas trouvé un bon moyen de le déterminer. Donc, même s'il est écrit correctement, j'ai besoin de savoir comment je testerais cela pour être sûr!
Voici ce que je fais en ce moment:
hdr_988=`head -c 988 ${inputFile}`
echo -n "${hdr_988}" > ${output_hdr}
headInput=`head -c 988 ${inputTrack} | hexdump`
headOutput=`head -c 988 ${output_hdr} | hexdump`
if [ "${headInput}" != "${headOutput}" ]; then echo "output header was not written properly. exiting. please troubleshoot."; exit 1; fi
Si j'utilise hexdump / xxd pour extraire cette partie du fichier, bien que je ne puisse pas en lire la plupart exactement, quelque chose semble mal. Et le code que j'ai écrit à titre de comparaison ne me dit que si deux chaînes sont identiques, pas si elles sont copiées comme je le souhaite.
Y a-t-il une meilleure façon de le faire en bash? Puis-je simplement copier / lire des octets binaires en natif-binaire, pour copier dans un fichier mot pour mot? (et idéalement pour le stockage en tant que variables également).
dd
pour copier des octets individuels (en le définissantcount
sur1
). Je ne suis pas sûr de les stocker, cependant.Réponses:
Traiter des données binaires à un bas niveau dans les scripts shell est généralement une mauvaise idée.
bash
les variables ne peuvent pas contenir l'octet 0.zsh
est le seul shell qui peut stocker cet octet dans ses variables.Dans tous les cas, les arguments de commande et les variables d'environnement ne peuvent pas contenir ces octets car ce sont des chaînes délimitées NUL passées à l'
execve
appel système.Notez également que:
ou sa forme moderne:
supprime tous les caractères de nouvelle ligne de fin de la sortie de
cmd
. Donc, si cette sortie binaire se termine par 0xa octets, elle sera altérée lorsqu'elle sera stockée dans$var
.Ici, vous devez stocker les données encodées, par exemple avec
xxd -p
.Vous pouvez définir des fonctions d'assistance comme:
xxd -p
la sortie n'est pas efficace en termes d'espace car elle code 1 octet en 2 octets, mais elle facilite les manipulations avec elle (concaténation, extraction de parties).base64
est celui qui code 3 octets en 4, mais n'est pas aussi facile à utiliser.Le
ksh93
shell a un format de codage intégré (utilisebase64
) que vous pouvez utiliser avec ses utilitairesread
etprintf
/print
:Maintenant, s'il n'y a pas de transit via des variables shell ou env, ou des arguments de commande, vous devriez être OK tant que les utilitaires que vous utilisez peuvent gérer n'importe quelle valeur d'octet. Mais notez que pour les utilitaires de texte, la plupart des implémentations non GNU ne peuvent pas gérer les octets NUL, et vous voudrez corriger les paramètres régionaux en C pour éviter les problèmes avec les caractères multi-octets. Le dernier caractère n'étant pas un caractère de nouvelle ligne peut également provoquer des problèmes ainsi que des lignes très longues (séquences d'octets entre deux octets 0xa qui sont plus longs
LINE_MAX
).head -c
où il est disponible devrait être OK ici, car il est censé fonctionner avec des octets, et n'a aucune raison de traiter les données comme du texte. Doncça devrait être bon. En pratique, au moins les implémentations intégrées GNU, FreeBSD et ksh93 sont OK. POSIX ne spécifie pas l'
-c
option, mais indique qu'ilhead
devrait prendre en charge les lignes de n'importe quelle longueur (sans s'y limiterLINE_MAX
)Avec
zsh
:Ou:
Même dans
zsh
, s'il$var
contient des octets NUL, vous pouvez le passer comme argument aux commandeszsh
internes (commeprint
ci-dessus) ou aux fonctions, mais pas comme arguments aux exécutables, car les arguments passés aux exécutables sont des chaînes délimitées par NUL, c'est une limitation du noyau, indépendante du shell.la source
zsh
n'est pas le seul shell qui peut stocker un ou plusieurs octets NUL dans une variable shell.ksh93
peut aussi le faire. En interne,ksh93
stocke simplement la variable binaire sous la forme d'une chaîne codée en base64.Hé bien oui. Mais peut-être devriez-vous considérer une raison très importante de NE PAS le faire. Fondamentalement, "bash" / "sh" / "csh" / "ksh" et similaires ne sont pas conçus pour le traitement de données binaires, et la plupart des utilitaires UNIX / LINUX standard ne le sont pas non plus.
Vous feriez mieux de vous en tenir à C ++ ou d'utiliser un langage de script comme Python, Ruby ou Perl capable de traiter des données binaires.
La meilleure façon est de ne pas le faire en bash.
la source
ffmpeg
,imagemagick
,dd
). Maintenant, si l'on fait de la programmation plutôt que de coller des choses ensemble, alors utiliser un langage de programmation complet est la voie à suivre.De votre question:
Si vous copiez 988 lignes, cela ressemble à un fichier texte, pas binaire. Cependant, votre code semble supposer 988 octets, pas 988 lignes, donc je suppose que les octets sont corrects.
Cette partie peut ne pas fonctionner. D'une part, tous les octets NUL du flux seront supprimés, car vous les utilisez
${hdr_988}
comme argument de ligne de commande et les arguments de ligne de commande ne peuvent pas contenir NUL. Les backticks pourraient également faire du munging d'espaces blancs (je n'en suis pas sûr). (En fait, comme ilecho
s'agit d'une fonction intégrée, la restriction NUL peut ne pas s'appliquer, mais je dirais qu'elle est encore incertaine.)Pourquoi ne pas simplement écrire l'en-tête directement du fichier d'entrée dans le fichier de sortie, sans le passer par une variable shell?
Ou, plus facilement,
Puisque vous mentionnez que vous utilisez
bash
, et non le shell POSIX, vous avez la substitution de processus à votre disposition, alors qu'en est-il comme test?Enfin: envisagez d' utiliser
$( ... )
au lieu de backticks.la source
dd
n'est pas nécessairement équivalent àhead
pour les fichiers non réguliers.head
fera autant d'read(2)
appels système que nécessaire pour obtenir ces 988 octets tandis qu'ildd
n'en fera qu'unread(2)
. GNUdd
aiflag=fullblock
pour essayer de lire ce bloc en entier, mais c'est encore moins portable quehead -c
.