bash не може зберігати hexvalue 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.
Kusalananda

Вам не вистачає цитат, але це має бути, 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 протягом десяти років і більше, я думаю, ви могли б дізнатись, що фактичною причиною є те, що оболонки використовували рядки в стилі С, а стандарт був побудований навколо цього.
giusti

Я знайшов хороший Q для детальної дискусії щодо printf проти відлуння. unix.stackexchange.com/questions/65803/…
Полб

8

Ви можете використовувати zshзамість цього єдину оболонку, яка може зберігати символ NUL у своїх змінних. Цей символ навіть трапляється за замовчуванням $IFSу zsh.

nul=$'\0'

Або:

nul=$'\x0'

Або

nul=$'\u0000'

Або

nul=$(printf '\0')

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

echo $'\0' # works
/bin/echo $'\0' # doesn't

1
"Ви можете використовувати zsh замість цього". Ні, дякую - я зараз навчаю себе баш-сценаріїв як початківець. Я не хочу плутати себе з іншим синтаксисом. Але велике спасибі, що запропонували це
Френк

Власне, zshу запитанні ви використовували синтаксис. echo -n $headerмати на увазі , щоб передати вміст $headerзмінної в якості останнього аргументу echo -nє zsh(або , fishабо , rcабо es) синтаксису, а НЕ bash синтаксис. У bash, це має зовсім інше значення . Більш загально zshце схоже на ksh( bash, оболонка GNU, будучи більш-менш частковим клоном kshоболонки де-факто Unix), але з більшою частиною дизайнерських ідіосинкрасій оболонки Борна виправлена ​​(і безліч додаткових функцій, і багато більш зручний / менш дивовижний).
Стефан Шазелас

Будьте уважні: zsh може іноді змінювати нульовий байт: echo $(printf 'ab\0cd') | od -vAn -tx1c друкує `61 62 20 63 64 0a`, тобто простір, де повинен існувати NUL.
Ісаак

1
І це щось, що жодна інша (жодна, нульова) оболонка не відтвориться. Це змушує сценарій поводитись в zsh дуже особливими способами. На мою думку: zsh просто намагається бути занадто розумним.
Ісаак

2
Якщо "виправити" дизайнерські нестандартності, присутні в стандарті POSIX sh, що звикнути писати zsh-скрипти означає, що ви звикаєте до практик, які були б баггі, якщо вони використовуються в будь-якій іншій оболонці. Це не така проблема з синтаксисом, який настільки на відміну від іншої мови, що навички чи звички, швидше за все, не передаються, але це не так.
Чарльз Даффі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.