Bash utilise des chaînes de style C en interne, qui se terminent par des octets nuls. Cela signifie qu'une chaîne Bash (telle que la valeur d'une variable ou un argument d'une commande) ne peut jamais contenir un octet nul. Par exemple, ce mini-script:
foobar=$'foo\0bar' # foobar='foo' + null byte + 'bar'
echo "${#foobar}" # print length of $foobar
imprime réellement 3
, car $foobar
est en réalité juste 'foo'
: le bar
vient après la fin de la chaîne.
De même, echo $'foo\0bar'
juste des impressions foo
, car echo
ne sait pas à propos de la \0bar
pièce.
Comme vous pouvez le constater, la \0
séquence est en réalité très trompeuse dans une $'...'
chaîne de style; cela ressemble à un octet nul à l'intérieur de la chaîne, mais cela ne fonctionne pas de cette façon. Dans votre premier exemple, votre read
commande a -d $'\0'
. Cela fonctionne, mais seulement parce que -d ''
fonctionne aussi! (Ce n’est pas une fonctionnalité explicitement documentée de read
, mais je suppose que cela fonctionne pour la même raison: ''
c’est la chaîne vide, donc son octet nul final vient immédiatement. Est documenté comme utilisant "Le premier caractère de délimitation ", et je suppose que cela fonctionne même si le "premier caractère" est passé la fin de la chaîne!)-d delim
Mais comme vous le savez dans votre find
exemple, il est possible qu'une commande imprime un octet nul et que cet octet soit acheminé vers une autre commande qui le lit en entrée. Aucune partie de cela ne repose sur le stockage d'un octet nul dans une chaîne à l'intérieur de Bash . Le seul problème avec votre deuxième exemple est que nous ne pouvons pas utiliser $'\0'
dans un argument à une commande; echo "$file"$'\0'
pourrait heureusement imprimer l'octet nul à la fin, si seulement il savait que vous le vouliez.
Donc, au lieu d'utiliser echo
, vous pouvez utiliser printf
, qui supporte les mêmes types de séquences d'échappement que les $'...'
chaînes de style. De cette façon, vous pouvez imprimer un octet nul sans avoir à en avoir un dans une chaîne. Cela ressemblerait à ceci:
for file in * ; do printf '%s\0' "$file" ; done \
| while IFS= read -r -d '' ; do echo "$REPLY" ; done
ou simplement ceci:
printf '%s\0' * \
| while IFS= read -r -d '' ; do echo "$REPLY" ; done
(Remarque: echo
dispose en fait d'un -e
indicateur lui permettant de traiter \0
et d'imprimer un octet nul; il essaiera ensuite de traiter les séquences spéciales de votre nom de fichier. L' printf
approche est donc plus robuste.)
Soit dit en passant, il y a des obus qui ne permettent des octets nuls chaînes à l' intérieur. Votre exemple fonctionne bien en Zsh, par exemple (en supposant les paramètres par défaut). Cependant, quel que soit votre shell, les systèmes d'exploitation de type Unix ne permettent pas d'inclure des octets nuls dans les arguments des programmes (car les arguments des programmes sont passés sous forme de chaînes de style C), il y aura donc toujours certaines limitations. (Votre exemple peut travailler dans zsh seulement parce echo
est une commande du shell, donc zsh peut l' invoquer sans compter sur le Si vous avez utilisé le soutien du système d'exploitation pour appeler d' autres programmes. Au command echo
lieu de echo
, de sorte qu'il contournée la commande interne et utilisé la version autonome de echo
programme sur le $PATH
, vous verriez le même comportement dans Zsh que dans Bash.)
-d ''
signifie déjà de délimiter\0
? J'ai trouvé une explication ici: stackoverflow.com/questions/8677546/…