Распространенный способ сделать что-то с парой файлов - и не бейте меня за это:
for f in $(ls); do …
Теперь, чтобы обезопасить себя от файлов с пробелами или другими странными символами, наивным способом было бы сделать:
find . -type f -print0 | while IFS= read -r -d '' file; …
Здесь -d ''
кратко для установки ASCII NUL, как в -d $'\0'
.
Но почему это так? Почему ''
и так $'\0'
же? Это из-за того, что C-корни Bash с пустой строкой всегда заканчиваются нулем?
for f in *
вместо разбораls
.for i in $(ls)
это ужасно глупо - мне почти стыдно, что я использовал это как плохой пример.find … -exec
вместо циклического обхода файлов, что работает в большинстве случаев, когда вместо этого вы используете такой цикл for. Здесьfind
позаботится обо всем для вас.Ответы:
man page of bash
Гласит:Поскольку строки обычно заканчиваются нулем, первым символом пустой строки является нулевой байт. - Имеет смысл для меня. :)
Источник гласит:
Для пустой строки
delim
это просто нулевой байт.источник
split()
она будет разделена между каждым символом. Я подозреваю, что «по историческим причинам» может быть лучшим объяснением, которое мы можем получить.'\0'
с'X\0'
должен дать вам'X\0'
, если все сделано правильно. Это не имеет ничего общего с функциями высокого уровня в таких языках, как JavaScript @dondelim = *list_optarg;
дает понять, почему это так.''
и$'\0'
то же самое?», Михас дал приблизительное объяснение «это то, что делает код». Я обрисовал альтернативный способ обработки пустой строки, который я считаю столь же разумным, и предположил, что выбор одного или другого был просто условностью или случайностью.В bash есть два недостатка, которые компенсируют друг друга.
Когда вы пишете
$'\0'
, это внутренне обрабатывается идентично пустой строке. Например:Это связано с тем, что внутренне bash хранит все строки как строки C , которые заканчиваются нулем - нулевой байт отмечает конец строки. Bash молча обрезает строку до первого нулевого байта (который не является частью строки!).
Когда вы передаете строку в качестве аргумента
-d
опцииread
встроенной функции, bash просматривает только первый байт строки. Но на самом деле он не проверяет, что строка не пуста. Внутренне, пустая строка представлена как байтовый массив из 1 элемента, который содержит только нулевой байт. Поэтому вместо чтения первого байта строки bash читает этот нулевой байт.Затем, внутренне, механизм позади
read
встроенного работает хорошо с нулевыми байтами; он продолжает читать побайтово, пока не найдет разделитель.Другие оболочки ведут себя по-другому. Например, ash и ksh игнорируют нулевые байты при чтении ввода. С ksh
ksh -d ""
читает до новой строки. Оболочки предназначены для того, чтобы хорошо справляться с текстом, а не с двоичными данными. Zsh является исключением: он использует строковое представление, которое справляется с произвольными байтами, включая нулевые байты; в zsh$'\0'
- строка длиной 1 (ноread -d ''
, как ни странно, ведет себя какread -d $'\0'
).источник
read
изменилось в bash 4.3, так что теперь оно пропускает нулевые байты. Например ,read x< <(printf a\\0a)
наборы ,x
чтобыaa
вместоa
.