Это очень надежно и поддерживается всеми версиями ядра, которые поддерживают initrd, AFAIK. Это особенность архивов cpio
, из которых состоят initramfs
. cpio
просто продолжает извлекать свои входные данные.... мы можем знать, что файл представляет собой два архива cpio один за другим, но cpio просто воспринимает их как один входной поток.
Debian советует использовать именно этот метод (добавление ещё одного cpio в initramfs) для добавления binary-blob прошивок в initramfs их установщика. Например:
DebianInstaller / NetbootFirmware | Debian Wiki
Initramfs по сути является конкатенацией gzipped cpio архивов которые извлекаются на ramdisk и используются как раннее пользовательское пространство ядром ядром Linux. initrd.gz установщика Debian на самом деле представляет собой один gzipped cpio архив, содержащий все файлы, необходимые программе установки при во время загрузки. Простым добавлением другого gzipped cpio архива - содержащий файлы прошивки, которых нам не хватает, мы получаем шоу на в путь!
Нечто подобное можно сделать в одной строке с:
$ a=-00100; a=${a%"${a#[+-]}"}${a#"${a%%[!0+-]*}"}; a=${a:-0}
$ echo "$a"
-100
Для 1000 повторений требуется всего 0,0482, что в 100 раз меньше, чем при использовании внешней программы.
Это основано на разложении двух двойных параметров:
${a#[+-]}
удалить первый символ, если это знак. ${a%"${a#[+-]}"}
сохраняет первый признак при условии, что это знак. ${a%%[!0+-]*}
удалить, начиная с любого (, а не с 0 или + или-)до конца. ${a#"${a%%[!0+-]*}"}
удалить вышеперечисленное, т. е. все ведущие нули и знаки. Это выбирает один знак и удаляет все ведущие нули. Однако позволяет (без ошибок):
Если эти тесты необходимы, продолжайте читать.
Количество знаков можно проверить с помощью:
signs=${a%%[!+-]*}
[ ${#signs} -gt 1 ] && echo "$0: Invalid number $a: Too many signs"
Тип допустимых символов можно было проверить с помощью:
num=${a#"${a%%[!0+-]*}"}
any=${num%%[!0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@_]*}
[ "$any" != "$num" ] && echo "$0: Invalid number $a"
hex=${num%%[!0123456789abcdefABCDEF]*}
[ "$hex" != "$num" ] && echo "$0: Invalid hexadecimal number $a"
dec=${num%%[!0123456789]*}
[ "$dec" != "$num" ] && echo "$0: Invalid decimal number $a"
И, наконец, мы можем воспользоваться возможностью printf
печатать предупреждение о числах «вне диапазона» (только для баз, которые printf понимает):
printf '%d' $sign$dec >/dev/null # for a decimal number
printf '%d' "${sign}0x$hex" >/dev/null # for hex numbers
Да, все printf используют %d
, это не опечатка.
И да, все вышеперечисленное работает в большинстве оболочек, имеющих printf
.
Обратите внимание, что хотя $((010))
требуется POSIX для расширения до 8, некоторые оболочки не делают этого по умолчанию (или только в некоторых контекстах ), если только не в режиме соответствия, поскольку это функция. Вы обычно не хотите.
При zsh
это контролируется параметром octalzeroes
(, отключенным по умолчанию, за исключением эмуляции sh/ksh ).
$ zsh -c 'echo $((010))'
10
$ zsh -o octalzeroes -c 'echo $((010))'
8
$ (exec -a sh zsh -c 'echo "$((010))"')
8
В mksh
это контролируется параметром posix
, (отключенным по умолчанию):
$ mksh -c 'echo "$((010))"'
10
$ mksh -o posix -c 'echo "$((010))"'
8
В bash нет возможности отключить его, но вы можете использовать синтаксис $((10#010))
ksh для принудительной интерпретации в десятичном формате (также работает в ksh и zsh ), хотя в bash
и mksh -o posix
, $((10#-010))
не работает (рассматривается как 10#0 - 010
, как видно из расширения $((-10#-010))
, дающего -8
), вам нужно$((-10#010))
(или $((- 10#010))
для совместимости с zsh
, который жалуется о том, что -10
является недействительной базой ).
$ bash -c 'echo "$((10#010))"'
10
С ksh93
сравните:
$ ksh93 -c 'echo "$((010))"'
8
$ ksh93 -c '((a = 010)); echo "$a"'
8
с:
$ ksh93 -c 'a=010; echo "$((a))"'
10
$ ksh93 -c 'printf "%d\n" 010'
10
$ ksh93 -c 'let a=010; echo "$a"'
10
$ ksh93 -c 'echo "$((010e0))"'
10
$ ksh93 -o letoctal -c 'let a=010; echo "$a"'
8
Так что, по крайней мере, если вы программируете конкретно для какой-либо из этих оболочек, есть способы обойти эту «ошибку».
Но ни один из них не поможет при написании переносимого сценария POSIX, и в этом случае вы захотите удалить начальные нули, как вы показали.
Вот ваш пример x1000 в моей системе:
$ cat shell.sh
#!/bin/dash
q=1
while [ "$q" -le 1000 ]
do
z=-00100
z=${z%"${z#[+-]}"}${z#"${z%%[!0+-]*}"}
z=${z:-0}
echo "$z"
q=$((q + 1))
done
Результат:
$ time./shell.sh >/dev/null
real 0m0.047s
Теперь я возражаю против примера с sed. Я вижу пример с файлом, но я не видя четкой причины, по которой использование файла неприемлемо. также пример с каналом проблематично, потому что канал не нужен -и не вызывает sed 1000 раз. если вы просто не можете использовать файл по какой-либо причине -, здесь будет документ отлично:
cat > sed.sh <<alfa
sed 's/^0*//' <<bravo
$(yes 00100 | head -1000)
bravo
alfa
Результат:
$ time./sed.sh >/dev/null
real 0m0.047s
Таким образом, в моей системе это точно такая же скорость без суеты.