bash не может хранить шестнадцатеричное значение 0x00 в переменной

11

Я пытаюсь сделать некоторые трюки с дд. Я думал, что было бы возможно сохранить некоторые шестнадцатеричные значения в переменной с именем "header", чтобы передать его в dd.

Мой первый шаг без переменной был такой:

$ echo -ne "\x36\xc9\xda\x00\xb4" |dd of=hex
$ hd hex

00000000  36 c9 da 00 b4                                    |6....|
00000005

После этого я попробовал это:

$ header=$(echo -ne "\x36\xc9\xda\x00\xb4") 
$ echo -n $header | hd

00000000  36 c9 da b4                                       |6...|
00000004

Как вы можете видеть, я потерял свое \x00значение в $headerпеременной. У кого-нибудь есть объяснение этому поведению? Это сводит меня с ума.

Фрэнк
источник
Я получаю bash: warning: command substitution: ignored null byte in input.
Кусалананда
Вы пропускаете кавычки, header="$(echo -ne "\x36\xc9\xda\x00\xb4")"; echo -n "$header" | hdно это просто дает тот же результат.
Ctrl-Alt-Delor
Это работает header="\x36\xc9\xda\x00\xb4"; echo -n "$header" | hd, но это не то же самое, что хранить читаемую человеком форму.
Ctrl-Alt-Delor

Ответы:

16

Вы не можете хранить нулевой байт в строке, потому что Bash использует строки в стиле C, которые резервируют нулевой байт для терминаторов. Поэтому вам нужно переписать ваш скрипт, чтобы просто передать последовательность, содержащую нулевой байт, без необходимости сохранения Bash в середине. Например, вы можете сделать это:

printf "\x36\xc9\xda\x00\xb4" | hd

Кстати, обратите внимание, что вам не нужно echo; Вы можете использовать Bash printfдля этого и многих других простых задач.

Или вместо цепочки вы можете использовать временный файл:

printf "\x36\xc9\xda\x00\xb4" > /tmp/mysequence
hd /tmp/mysequence

Конечно, это проблема того, что файл /tmp/mysequenceможет уже существовать. И теперь вам нужно продолжать создавать временные файлы и сохранять их пути в строках.

Или вы можете избежать этого, используя подстановку процесса:

hd <(printf "\x36\xc9\xda\x00\xb4")

<(command)Оператор создает именованный канал в файловой системе, которая получит выход command. hdв качестве первого аргумента получит путь к этому каналу, который он откроет и прочитает почти как любой файл. Вы можете прочитать больше об этом здесь: /unix//a/17117/136742 .

Джусти
источник
1
Несмотря на правильность, это деталь реализации, а не точная причина. Я посмотрел на это, и стандарт POSIX фактически требует такого поведения, поэтому у вас есть реальная причина. (Как некоторые уже указывали, zshбудет делать это, но только в режиме nōn-POSIX.) Я действительно изучил это, потому что мне было интересно, стоило ли это реализовать в mksh
mirabilos
@mirabilos, не могли бы вы рассказать об этом подробнее? AFAICT, поведение не определено для каждого POSIX для подстановки команд, когда выходные данные имеют символы NUL, и для zsh в режиме POSIX единственное существенное различие, о котором я могу думать, состоит в том, что в shэмуляции \0нет значения по умолчанию $IFS. echo "$(printf 'a\0b')"все еще работает нормально в shэмуляции в zsh.
Стефан
3
@mirabilos Учитывая, что оболочки предшествуют стандарту POSIX на десять или более лет, я думаю, вы могли бы узнать, что на самом деле причина в том, что оболочки используют строки в стиле C, и стандарт был построен на этом.
giusti
Я нашел хороший вопрос для подробного обсуждения printf против echo. unix.stackexchange.com/questions/65803/…
Полб
8

zshВместо этого вы можете использовать единственную оболочку, которая может хранить символ NUL в своих переменных. Этот символ даже в значении по умолчанию $IFSв zsh.

nul=$'\0'

Или:

nul=$'\x0'

Или

nul=$'\u0000'

Или

nul=$(printf '\0')

Однако обратите внимание, что вы не можете передать такую ​​переменную как аргумент или переменную окружения команде, которая выполняется как аргументы, а переменные окружения - это разделенные NUL строки, передаваемые execve()системному вызову (ограничение системного API, а не оболочки ). Однако zshвы можете передавать байты NUL в качестве аргументов в функции или встроенные команды.

echo $'\0' # works
/bin/echo $'\0' # doesn't
Стефан Шазелас
источник
1
Msgstr "Вы можете использовать вместо zsh". Нет, спасибо, я сейчас учу себя написанию скриптов на bash. Я не хочу путать себя с другим синтаксисом. Но большое спасибо за то, что предложили это
Фрэнк
На самом деле, вы использовали zshсинтаксис в своем вопросе. echo -n $headerиметь в виду , чтобы передать содержимое $headerпеременной в качестве последнего аргумента echo -nявляется zsh(или , fishили , rcили es) синтаксиса, а не bash синтаксис. Во bash, это имеет совсем другое значение . В более общем смысле zshэто похоже на ksh( bashоболочка GNU, являющаяся более или менее частичным клоном ksh, оболочка де-факто Unix), но с большинством конструктивных особенностей оболочки Bourne исправлены (и много дополнительных функций, и много более удобный / менее удивительный).
Стефан
Будьте осторожны: zsh иногда может изменить нулевой байт: echo $(printf 'ab\0cd') | od -vAn -tx1c печатает `61 62 20 63 64 0a`, это место, где должен существовать NUL.
Исаак
1
И это то, что никакая другая (нет, ноль) оболочка не будет воспроизводить. Это заставляет скрипт вести себя совершенно по-особенному в zsh. На мой взгляд: Zsh просто пытается быть слишком умным.
Исаак
2
«Исправив» недостатки дизайна, присутствующие в стандарте POSIX sh, что привыкание к написанию сценариев zsh означает, что человек привыкнет к практикам, которые могут привести к ошибкам при использовании в любой другой оболочке. Это не такая проблема с синтаксисом, который настолько непохож на другой язык, который навыки или привычки вряд ли передадут, но дело обстоит не так.
Чарльз Даффи