Attribuer une chaîne contenant un caractère nul () à une variable dans Bash

en essayant de traiter correctement une liste de noms de fichiers-/foldernames ( voir mes autres questions ) en utilisant un caractère nul comme délimiteur, je suis tombé sur un comportement étrange de Bash que je ne comprends pas:

lors de l'affectation d'une chaîne contenant un ou plusieurs caractères nuls à une variable, les caractères nuls sont perdus / ignorés / non stockés.

par exemple,

echo -ne "n"151900920"m"151900920"k" | od -c   # -> 0000000   n  "151900920"   m  "151900920"   k

But:

VAR1=`echo -ne "n"151910920"m"151910920"k"`
echo -ne "$VAR1" | od -c   # -> 0000000   n   m   k

cela signifie que j'aurais besoin d'écrire cette chaîne de caractères dans un fichier (par exemple, in /tmp) et de la relire à partir de là si la tuyauterie directe n'est pas souhaitée ou faisable.

lors de l'exécution de ces scripts dans z shell (zsh) les chaînes contenant sont préservées dans les deux cas, mais malheureusement je ne peux pas supposer que zsh est présent dans les systèmes exécutant mon script alors que Bash devrait l'être.

comment les cordes contenant des caractères stocker ou traiter efficacement sans perdre de caractères (meta -)?

23
demandé sur Community 2011-07-04 14:57:52

4 réponses

dans Bash, vous ne pouvez pas stocker le caractère nul dans une variable.

vous pouvez, cependant, stocker un simple vidage hexadécimal des données (et plus tard inverser cette opération à nouveau) en utilisant la commande xxd .

VAR1=`echo -ne "n"151900920"m"151900920"k" | xxd -p | tr -d '\n'`
echo -ne "$VAR1" | xxd -r -p | od -c   # -> 0000000    n  "151900920"   m  "151900920"   k
30
répondu jeff 2011-07-04 12:32:22

comme d'autres l'ont déjà déclaré, vous ne pouvez pas stocker / utiliser NUL char :

  • dans une variable
  • dans un argument de la ligne de commande.

Toutefois, que vous pouvez manipuler des données binaires (y compris NUL char):

  • dans les tuyaux
  • dans les fichiers

Afin de répondez à votre dernière question:

est-ce que quelqu'un peut me donner un indice comment les chaînes contenant des caractères \0 peuvent être stocké ou manipulé efficacement sans perdre aucun caractère (meta -)?

Vous pouvez utilisation des fichiers ou des tuyaux à conserver et à traiter efficacement n'importe quelle chaîne du méta-caractères.

si vous prévoyez de traiter des données, vous devez noter En outre que:

limitations de contournement

si vous voulez utiliser les variables, alors vous devez vous débarrasser du char NUL en l'encodant, et diverses autres solutions ici donnent des façons intelligentes de le faire (une façon évidente est d'utiliser par exemple l'encodage/décodage base64).

si vous êtes concerné par la mémoire ou la vitesse, vous aurez probablement envie d'utiliser un analyseur minimal et ne citer que le caractère NUL (et le caractère de citation). Dans ce cas, cela vous aiderait:

quote() { sed 's/\/\\/g;s/\x0/\0/g'; }

Ensuite, vous pouvez sécuriser vos données avant de les stocker dans les variables et les argument de ligne de commande en pipant vos données sensibles dans quote , qui produira un flux de données sûr sans caractères noirs. Vous pouvez revenir en arrière la chaîne originale (avec des barres nulles) en utilisant echo -en "$var_quoted" qui enverra la chaîne correcte sur la sortie standard.

exemple:

## Our example output generator, with NUL chars
ascii_table() { echo -en "$(echo '\'0{0..3}{0..7}{0..7} | tr -d " ")"; }
## store
myvar_quoted=$(ascii_table | quote)
## use
echo -en "$myvar_quoted"

Remarque: utilisez | hd pour obtenir un nettoyage de vos données en hexadécimal et vérifiez que vous n'avez pas perdu de dollars.

Changement d'outils

souvenez-vous que vous pouvez aller assez loin avec des pipes sans variables en utilisant nor argument dans la ligne de commande, n'oubliez pas par exemple la construction <(command ...) qui créera un pipe nommé (sorte de fichier temporaire).

EDIT: la première mise en œuvre de quote était incorrect et ne permettrait pas de traiter correctement avec \ caractères spéciaux interprété par echo -en . Merci @xhienne pour s'apercevoir qu'.

15
répondu vaab 2018-02-13 01:14:45

utiliser uuencode et uudecode pour la portabilité POSIX

xxd et base64 ne sont pas POSIX 7 mais uuencode est .

VAR="$(uuencode -m <(printf "a"151900920"\n") /dev/stdout)"
uudecode -o /dev/stdout <(printf "$VAR") | od -tx1

sortie:

0000000 61 00 0a
0000003

malheureusement, je ne vois pas D'alternative POSIX 7 pour le processus de Bash <() extension de substitution sauf écrire au fichier, et ils ne sont pas installés dans Ubuntu 12.04 par défaut (paquet sharutils ).

donc je suppose que la vraie réponse est: n'utilisez pas Bash pour cela, utilisez Python ou un autre langage interprété par saner.

8

j'aime de jeff réponse . J'utiliserais L'encodage Base64 au lieu de xxd. Il économise un peu d'espace et serait (je pense) plus reconnaissable quant à ce qui est prévu.

VAR=$(echo -n "foo"151900920"bar" | base64)
echo -n $VAR | base64 -d | xargs -0 ...

quant à-e, il n'est pas nécessaire car le shell interprète déjà l'évasion avant même qu'elle ne fasse écho. Je me souviens aussi de quelque chose à propos de "echo-e" étant dangereux si vous faites écho à n'importe quelle entrée de l'utilisateur car ils pourraient injecter des séquences d'échappement que echo interprétera et finir avec de mauvaises choses.

3
répondu vontrapp 2017-05-23 10:30:55