Bash utilizza stringhe di tipo C internamente, che sono terminate da byte null. Ciò significa che una stringa di Bash (come il valore di una variabile o un argomento di un comando) non può mai contenere un byte null. Ad esempio, questo mini-script:
foobar=$'foo\0bar' # foobar='foo' + null byte + 'bar'
echo "${#foobar}" # print length of $foobar
in realtà stampa 3
, perché in $foobar
realtà è solo 'foo'
: bar
arriva dopo la fine della stringa.
Allo stesso modo, echo $'foo\0bar'
stampa solo foo
perché echo
non conosce la \0bar
parte.
Come puoi vedere, la \0
sequenza è in realtà molto fuorviante in una $'...'
stringa di tipo; sembra un byte nullo all'interno della stringa, ma non finisce per funzionare in questo modo. Nel tuo primo esempio, il tuo read
comando ha -d $'\0'
. Funziona, ma solo perché -d ''
funziona anche! (Questa non è una caratteristica esplicitamente documentata di read
, ma suppongo che funzioni per lo stesso motivo: ''
è la stringa vuota, quindi il suo byte null di terminazione arriva immediatamente. È documentato come usando "Il primo carattere del delim ", e immagino che funzioni anche se il "primo carattere" è oltre la fine della stringa!)-d delim
Ma come sai dal tuo find
esempio, è possibile che un comando stampi un byte nullo e che quel byte sia reindirizzato a un altro comando che lo legge come input. Nessuna parte di ciò si basa sulla memorizzazione di un byte null in una stringa all'interno di Bash . L'unico problema con il tuo secondo esempio è che non possiamo usare $'\0'
in un argomento un comando; echo "$file"$'\0'
potrebbe felicemente stampare il byte null alla fine, se solo sapesse che lo volevi.
Quindi, invece di utilizzare echo
, è possibile utilizzare printf
, che supporta gli stessi tipi di sequenze di escape delle $'...'
stringhe di stile. In questo modo, puoi stampare un byte null senza dover avere un byte null all'interno di una stringa. Sarebbe così:
for file in * ; do printf '%s\0' "$file" ; done \
| while IFS= read -r -d '' ; do echo "$REPLY" ; done
o semplicemente questo:
printf '%s\0' * \
| while IFS= read -r -d '' ; do echo "$REPLY" ; done
(Nota: in echo
realtà ha anche un -e
flag che gli permetterebbe di elaborare \0
e stampare un byte null; ma poi proverebbe anche a elaborare eventuali sequenze speciali nel tuo nome file. Quindi l' printf
approccio è più robusto.)
Per inciso, ci sono alcune conchiglie che non consentono nulla byte stringhe all'interno. Il tuo esempio funziona bene in Zsh, ad esempio (assumendo le impostazioni predefinite). Tuttavia, indipendentemente dalla shell, i sistemi operativi simili a Unix non forniscono un modo per includere byte nulli all'interno degli argomenti dei programmi (poiché gli argomenti del programma vengono passati come stringhe in stile C), quindi ci saranno sempre delle limitazioni. (Il tuo esempio può funzionare in Zsh solo perché echo
è un built-in della shell, quindi Zsh può invocarlo senza fare affidamento sul supporto del sistema operativo per invocare altri programmi. Se lo hai utilizzato command echo
invece di echo
, in modo da bypassare il builtin e utilizzare il echo
programma autonomo sul $PATH
, vedresti lo stesso comportamento in Zsh che in Bash.)
-d ''
già intende delimitare\0
? Ho trovato una spiegazione qui: stackoverflow.com/questions/8677546/...