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 / ...