Как я могу работать с двоичным кодом в bash, чтобы копировать байты дословно без преобразования?

Pbrun: Терминал защищен и является продуктом SUN.
sudo: пользователь защищен вместо терминала, и это продукт Solaris.

14
24.04.2016, 01:06
3 ответа

Я амбициозно пытаюсь перевести код на c++ в bash по множеству причин.

Ну да. Но, возможно, вам стоит подумать об очень важной причине НЕ делать этого. В принципе, "bash" / "sh" / "csh" / "ksh" и тому подобное не предназначены для обработки двоичных данных, как и большинство стандартных утилит UNIX / LINUX.

Вам лучше либо придерживаться C++, либо использовать скриптовый язык, такой как Python, Ruby или Perl, который способен работать с двоичными данными.

Есть ли лучший способ сделать это в bash?

Лучший способ - не делать это в bash.

11
27.01.2020, 19:50

Из вашего вопроса:

скопируйте первые 988 строк заголовка

Если вы копируете 988 строк, это похоже на текстовый файл , а не двоичный. Однако ваш код, похоже, предполагает 988 байт, а не 988 строк, поэтому я предполагаю, что байты верны.

hdr_988=`head -c 988 ${inputFile}`
echo -n "${hdr_988}" > ${output_hdr}

Эта часть может не работать. Во-первых, любые байты NUL в потоке будут удалены, потому что вы используете $ {hdr_988} в качестве аргумента командной строки, а аргументы командной строки не могут содержать NUL. Обратные кавычки могут также изменять пробелы (я не уверен в этом). (На самом деле, поскольку echo является встроенным, ограничение NUL может не применяться, но я бы сказал, что это все еще сомнительно.)

Почему бы просто не записать заголовок непосредственно из входного файла в выходной файл, не передавая его через переменную оболочки?

head -c 988 "${inputFile}" >"${output_hdr}"

Или, что более переносимо,

dd if="${inputFile}" of="${output_hdr}" bs=988 count=1

Поскольку вы упомянули, что используете bash , а не оболочку POSIX, у вас есть возможность подстановки процессов, так как насчет этого в качестве теста?

cmp <(head -c 988 "${inputFile}") <(head -c 988 "${output_hdr}")

Наконец: рассмотрите , используя $ (...) вместо обратные кавычки.

6
27.01.2020, 19:50

Работа с двоичными данными на низком уровне в сценариях оболочки обычно является плохой идеей.

Переменные bash не могут содержать байт 0. zsh - единственная оболочка, которая может хранить этот байт в своих переменных.

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

Также обратите внимание, что:

var=`cmd`

или его современная форма:

var=$(cmd)

удаляет все завершающие символы новой строки из вывода cmd . Итак, если этот двоичный вывод заканчивается байтами 0xa, он будет искажен при сохранении в $ var .

Здесь вам нужно сохранить закодированные данные, например, с помощью xxd -p .

hdr_988=$(head -c 988 < "$inputFile" | xxd -p)
printf '%s\n' "$hdr_988" | xxd -p -r > "$output_hdr"

Вы можете определить вспомогательные функции, такие как:

encode() {
  eval "$1"='$(
    shift
    "$@" | xxd -p  -c 0x7fffffff
    exit "${PIPESTATUS[0]}")'
}

decode() {
  printf %s "$1" | xxd -p -r
}

encode var cat /bin/ls &&
  decode "$var" | cmp - /bin/ls && echo OK

xxd -p output не занимает много места, поскольку он кодирует 1 байт в 2 байта, но упрощает манипуляции с ним (объединение, извлечение частей). base64 - это тот, который кодирует 3 байта в 4, но с ним не так просто работать.

Оболочка ksh93 имеет встроенный формат кодирования (использует base64 ), который вы можете использовать с его read и printf / print utilities:

typeset -b var # marked as "binary"/"base64-encoded"
IFS= read -rn 988 var < input
printf %B var > output

Теперь, если нет передачи через переменные оболочки, env или аргументы команд, все должно быть в порядке, пока используемые вами утилиты могут обрабатывать любое байтовое значение. Но обратите внимание, что для текстовых утилит большинство реализаций, отличных от GNU, не могут обрабатывать байты NUL, и вы захотите исправить языковой стандарт на C, чтобы избежать проблем с многобайтовыми символами. Последний символ, не являющийся символом новой строки, также может вызывать проблемы, а также очень длинные строки (последовательности байтов между двумя байтами 0xa, которые длиннее, чем LINE_MAX ).

head -c там, где он доступен, здесь должно быть нормально, так как он предназначен для работы с байтами и не имеет причин рассматривать данные как текст. Так что

head -c 988 < input > output

должно быть в порядке. На практике, по крайней мере, встроенные реализации GNU, FreeBSD и ksh93 в порядке. POSIX не указывает параметр -c , но говорит, что head должен поддерживать строки любой длины (не ограничиваясь LINE_MAX )

С zsh :

IFS= read -rk988 -u0 var < input &&
print -rn -- $var > output

Или:

var=$(head -c 988 < input && echo .) && var=${var%.}
print -rn -- $var > output

Даже в zsh , если $ var содержит байты NUL, вы можете передать его в качестве аргумента zsh встроенные функции (например, print выше) или функции, но не как аргументы исполняемым файлам, поскольку аргументы, передаваемые исполняемым файлам, представляют собой строки с разделителями NUL, что является ограничением ядра, не зависящим от оболочки.

23
27.01.2020, 19:50

Теги

Похожие вопросы