Странная проблема с printf в сценарии bash:"09" и "08" являются недопустимыми номерами, "07" и " 06 " в порядке

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

6 ответов


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

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

почему это поможет? Ну, буквальное число, начинающееся с 0 но не имея x на 2-м месте интерпретируется как восьмеричное значение.

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

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

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


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

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

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


синтаксис числовой арифметической оценки Баша (( ... )) можно преобразовать в базу 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

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

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


просто добавить к Оли!--5-->, для того, чтобы дополнить число нулями достаточно поставить 0 после %, как:

printf "%04d" "9"


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

printf "%04.f" "009"

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


добавлять * делает расширение параметров оболочки жадным (см., например,Shell Tipps: используйте внутреннюю обработку строк)!

# strip leading 0s
- a="009"; echo ${a##0}
+ a="009"; echo ${a##*0}