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
послідовність насправді дуже вводить в оману в $'...'
рядку -style; це виглядає як нульовий байт всередині рядка, але він не закінчується таким чином. У вашому першому прикладі ваша read
команда має -d $'\0'
. Це працює, але тільки тому, що -d ''
також працює! (Це не явно задокументована функція read
, але я вважаю, що вона працює з тієї ж причини: ''
це порожній рядок, тому його завершальний нульовий байт надходить негайно. Документовано як використання "Перший символ delim ", і я здогадуюсь, що навіть працює якщо "перший символ" минулий кінець рядка!)-d delim
Але , як ви знаєте з find
прикладу, що це можливо для команди , щоб роздрукувати нульові байти, а для цього байта бути переданий в іншу команду , яка зчитує його в якості вхідних даних. Жодна частина цього не покладається на збереження нульового байта в рядку всередині Bash . Єдина проблема у вашому другому прикладі полягає в тому, що ми не можемо використовувати $'\0'
аргумент до команди; echo "$file"$'\0'
міг із задоволенням надрукувати нульовий байт наприкінці, якби тільки знав, що ти цього хочеш.
Таким чином, замість використання echo
ви можете використовувати printf
, що підтримує ті ж види послідовностей втечі, що й $'...'
рядки -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 / ...