Необычная проблема с 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 вызывают проблему: каждый другой номер в моей последовательности выглядит нормально.
Ответы
Ответ 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"
Ответ 6
Добавление *
делает расширение параметра оболочки совпадающим с жадным (см., например, Shell Tipps: используйте внутреннюю обработку строк)!
# strip leading 0s
- a="009"; echo ${a##0}
+ a="009"; echo ${a##*0}
Ответ 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 типов людей: те, кто понимает двоичный код, и те, кто не понимает.
Использование bash Расширения параметров
Для этого вам нужно использовать переменную
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