Bash внутренне использует строки в стиле C, которые заканчиваются нулевыми байтами. Это означает, что строка Bash (например, значение переменной или аргумент команды) фактически не может содержать нулевой байт. Например, этот мини-скрипт:
foobar=$'foo\0bar' # foobar='foo' + null byte + 'bar'
echo "${#foobar}" # print length of $foobar
на самом деле печатает 3, потому что $foobarна самом деле просто 'foo': barидет после конца строки.
Точно так же echo $'foo\0bar'просто печатает foo, потому echoчто не знает о \0barчасти.
Как видите, \0последовательность на самом деле очень вводит в заблуждение в $'...'строке -стиля; он выглядит как нулевой байт внутри строки, но в итоге он не работает таким образом. В вашем первом примере ваша readкоманда имеет -d $'\0'. Это работает, но только потому, что -d ''тоже работает! (Это явно не задокументированная особенность read, но я предполагаю, что она работает по той же причине: ''это пустая строка, поэтому ее завершающий нулевой байт приходит сразу. Задокументировано как использование «первого символа разделителя », и я предполагаю, что даже работает если «первый символ» находится за концом строки!)-d delim
Но , как вы знаете из findпримера, что это возможно для команды , чтобы распечатать нулевые байты, а для этого байта быть передан в другую команду , которая считывает его в качестве входных данных. Никакая часть этого не полагается на сохранение нулевого байта в строке внутри Bash . Единственная проблема с вашим вторым примером заключается в том, что мы не можем использовать $'\0'аргумент команды; echo "$file"$'\0'мог бы счастливо напечатать нулевой байт в конце, если бы знал, что вы этого хотите.
Таким образом, вместо использования echoвы можете использовать printf, который поддерживает те же виды escape-последовательностей, что и $'...'строки -style. Таким образом, вы можете напечатать нулевой байт без необходимости иметь нулевой байт внутри строки. Это будет выглядеть так:
for file in * ; do printf '%s\0' "$file" ; done \
| while IFS= read -r -d '' ; do echo "$REPLY" ; done
или просто так:
printf '%s\0' * \
| while IFS= read -r -d '' ; do echo "$REPLY" ; done
(Примечание: на echoсамом деле также есть -eфлаг, который позволил бы ему обрабатывать \0и печатать нулевой байт; но затем он также пытался бы обрабатывать любые специальные последовательности в вашем имени файла. Таким образом, printfподход является более надежным.)
Кстати, есть некоторые оболочки , которые действительно позволяют нулевой байт внутри строки. Например, ваш пример отлично работает в Zsh (при условии настроек по умолчанию). Однако, независимо от вашей оболочки, Unix-подобные операционные системы не обеспечивают способ включения нулевых байтов в аргументы программ (поскольку аргументы программы передаются в виде строк в стиле C), поэтому всегда будут некоторые ограничения. (Ваш пример может работать в Zsh только потому , что echoэто встроенная команда оболочки, так Zsh может вызвать его , не полагаясь на поддержку ОС для вызова других программ. Если вы использовали command echoвместо echo, так что обойти встроенную и использовать автономную echoпрограмму на $PATH, вы увидите то же поведение в Zsh, что и в Bash.)
-d ''уже означает разделение на\0? Я нашел объяснение здесь: stackoverflow.com/questions/8677546/…