Странная проблема с printf в bash-скрипте: «09» и «08» - недопустимые числа, «07» и «06» - в порядке - PullRequest
25 голосов
/ 10 ноября 2011

Это мой bash-скрипт - я просто хочу набрать с нуля набор чисел с нулями:

printf "%04d" "09"
printf "%04d" "08"
printf "%04d" "07"
printf "%04d" "06"

Выход:

./rename.sh: line 3: printf: 09: invalid number 
0000
./rename.sh: line 4: printf: 08: invalid number 
0000 
0007
0006

Что ...?

Только 09 и 08 вызывают проблему: все остальные числа в моей последовательности, кажется, в порядке.

Ответы [ 8 ]

27 голосов
/ 10 ноября 2011

Если у вас есть "09" в переменной, вы можете сделать

a="09"
echo "$a"
echo "${a#0}"
printf "%04d" "${a#0}"

Почему это помогает?Ну, числовой литерал, начинающийся с 0, но не имеющий x на втором месте, интерпретируется как восьмеричное значение.

Восьмеричное значение имеет только цифры 0 .. 7, 8и 9 неизвестны.

"${a#0}" лишает одного ведущего 0.Результирующее значение может быть передано printf затем, что печатает его соответствующим образом, с префиксом 0, в 4 цифры.

Если вы ожидаете, что вы получите значения, такие как "009", вещи получаютсяболее сложным, так как вам придется использовать цикл, который устраняет все лишние 0 s в начале, или выражение extglob, как упомянуто в комментариях.

20 голосов
/ 10 ноября 2011

Числа, начинающиеся с "0", обрабатываются как восьмеричные (т. Е. Base-8). Следовательно, «8» и «9» не являются действительными цифрами.

См. http://www.gnu.org/software/bash/manual/bashref.html#Shell-Arithmetic.

Это поведение унаследовано от таких языков, как C.

18 голосов
/ 04 августа 2012

Синтаксис числовой арифметической оценки Bash (( ... )) может преобразовываться в основание 10 (для обеспечения правильной интерпретации) со следующим синтаксисом: (( 10#$var )). Или, в случае необработанного числа: (( 10#08 )). Очень простой и чистый, и его можно использовать везде, где вы уверены, что основание должно быть 10, но не можете гарантировать, что начальный ноль не будет включен.

Итак, в вашем примере это будет выглядеть так:

printf "%04d\n" $(( 10#09 ))
printf "%04d\n" $(( 10#08 ))
printf "%04d\n" $(( 10#07 ))
printf "%04d\n" $(( 10#06 ))

Производит следующий вывод:

0009
0008
0007
0006

С этим синтаксисом, поскольку вы затем работаете со значением переменной вместо самой переменной, инкременты (( var++ )) и декременты (( var-- )) не будут работать, но все еще могут быть относительно чисто реализованы как var=$(( 10#var + 1 )) и var=$(( 10#var - 1 )) соответственно.

Я впервые столкнулся с этим решением здесь , но этот ответ на аналогичный вопрос переполнения стека также демонстрирует его.

4 голосов
/ 10 ноября 2011

Просто чтобы добавить к ответ Оли , чтобы дополнить число нулями, достаточно поставить 0 после %, как вы это сделали:

printf "%<b><i>0</i></b>4d" "9"

3 голосов
/ 12 февраля 2013

Плавающая точка обрабатывается по-разному:

printf "%04.f" "009"

Это дает правильный вывод без каких-либо причудливых ошибок (согласно ответу @Oli Charlesworth, 0 *** считается восьмеричным, но я считаю, что Bash игнорирует восьмеричные / шестнадцатеричные идентификаторы для чисел с плавающей запятой)

1 голос
/ 02 марта 2019

Почему «09» и «08» являются недопустимыми числами, «07» и «06» хороши

Поскольку предшествующее целое число 0 является обычным сокращением, означающим числовосьмеричное .

printf "%d\n" 010 0100
8
64

printf "%o\n" 8 64 0100
10
100
100
printf "%04o\n" 8 64 0100
0010
0100
0100

Как с этим работать?

Использование целого числа bash: перед числом 10#

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

echo $(( 10#0100))
100
echo $(( 10#0900))
900
echo $(( 10#0900 ))
900

и

echo $(( 8#100 ))
64
echo $(( 2#100 ))
4

Конечно!

ТамВ мире всего 10 типов людей: те, кто понимает двоичный код, и те, кто не понимает.

Использование Расширение параметров

Для этого вам нужно использовать переменную

a=09
echo ${a#0}
9

Это будет работать нормально, пока у вас не останется только один 0 для сброса.

a=0000090

echo ${a#*0}
000090
echo ${a##0}
000090

Для этого вы можете использовать extglob функцию:

echo ${a##*(0)}
0000090
shopt -s extglob
echo ${a##*(0)}
90

Использование floating point с printf

printf "%.0f\n" 09
9

Мне нравится использовать -v параметр printf для установки некоторых ваriable:

printf -v a %.0f 09
echo $a
9

или даже если

a=00090
printf -v a %.0f $a
echo $a
90
1 голос
/ 21 ноября 2014

Добавление * делает расширение параметров оболочки соответствующим жадному алгоритму (см., Например, Типы оболочки: используйте внутреннюю обработку строк )!

# strip leading 0s
- a="009"; echo ${a##0}
+ a="009"; echo ${a##*0}
0 голосов
/ 31 мая 2019

Гораздо проще преобразовать из восьмеричного в десятичное: $ nm = $ ((10 # $ nm))

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...