Подтвердить что ты не робот

Необычная проблема с printf в bash script: "09" и "08" - недопустимые числа, "07" и "06" - штраф

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

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 вызывают проблему: каждый другой номер в моей последовательности выглядит нормально.

4b9b3361

Ответ 1

Если у вас есть "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 в начале или выражение extglob, как указано в комментариях.

Ответ 2

Цифры, начинающиеся с "0", обрабатываются как восьмеричные (т.е. база-8). Поэтому "8" и "9" являются недопустимыми цифрами.

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

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

Ответ 3

Синтаксис 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

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

printf "%04.f" "009"

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

Ответ 5

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

printf "%04d" "9"

Ответ 7

Почему "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 для установки некоторой переменной:

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

или даже если

a=00090
printf -v a %.0f $a
echo $a
90

Ответ 8

Гораздо проще преобразовать из восьмеричного в десятичное:

nm=$((10#$nm))

Ответ 9

если a=079 сначала удалите конечные нули с помощью

a='echo $a | sed 's/^0*//''

затем делайте нулю

printf "%04d" $a