Как обнулить последовательность целых чисел в bash, чтобы все они имели одинаковую ширину?


429

Мне нужно зациклить некоторые значения,

for i in $(seq $first $last)
do
    does something here
done

Для $firstи $lastмне нужно, чтобы он был фиксированной длины 5. Так что, если ввод 1, мне нужно добавить нули перед таким, чтобы он стал 00001. Это петли до 99999, например, но длина должна быть 5.

Например: 00002, 00042, 00212, 012312и так далее.

Любая идея о том, как я могу это сделать?


22
Хороший ответ может быть следующим: seq - w 1 99999. Опция -w поддерживает постоянную длину вывода, добавляя кратчайшие числа с 0.
Bruno von Paris


Многие ответы здесь рекомендуют, for variable in $(something to generate the numbers); do ...но это проблематично, когда список чисел длинный. Это гораздо эффективнее в использовании something to generate the numbers | while read -r variable; do .... Смотрите также mywiki.wooledge.org/DontReadLinesWithFor, в котором обсуждается чтение строк из файлов и т. Д., Но некоторые аргументы применимы и здесь.
tripleee

Ответы:


713

Хотя в вашем конкретном случае, вероятно, проще всего использовать -fфлаг, seqчтобы заставить его форматировать числа при выводе списка. Например:

for i in $(seq -f "%05g" 10 15)
do
  echo $i
done

выдаст следующий вывод:

00010
00011
00012
00013
00014
00015

В более общем смысле, bashимеет printfвстроенную функцию, так что вы можете дополнить вывод нулями следующим образом:

$ i=99
$ printf "%05d\n" $i
00099

Вы можете использовать -vфлаг, чтобы сохранить выходные данные в другой переменной:

$ i=99
$ printf -v j "%05d" $i
$ echo $j
00099

Обратите внимание, что printfподдерживается немного другой формат, seqпоэтому вам нужно использовать %05dвместо %05g.


5
%05gвместо того, чтобы %05dисправить это для меня. «00026» давал мне «00022» . Спасибо!
Эд Мане

12
Отличный, исчерпывающий ответ. Одно небольшое исправление: строго говоря, seqподдерживает подмножество символов формата. который printfподдерживает (и что включает в себя подмножества g, но не d). Поведение персонажей dи gотличается тонко в том , что dинтерпретирует 0-prefixed числа строк в восьмеричном чисел и преобразует их в десятичной системе , а gрассматривает их как десятичные дроби. (@EdManet: именно поэтому «00026» превратился в «00022» с d). Еще одна вещь, о которой стоит упомянуть: seq -wвыполняет автоматическое заполнение нулями выходных чисел на основе самого широкого сгенерированного числа.
mklement0

Это помогло мне создать список шестнадцатеричных символов. for (( i = 0; i <= 0xffffffff; i++ )) do printf "%08x\n" $i ; done >> hex.txtсоздал 8-значный шестнадцатеричный список. Спасибо.
cd

seq --help: «FORMAT должен быть пригоден для печати одного аргумента типа 'double'; по умолчанию используется% .PRECf, если FIRST, INCREMENT и LAST являются десятичными числами с фиксированной запятой с максимальной точностью PREC и% g в противном случае." Возможные спецификаторы преобразования являются efgaEFGA.
x-

133

Проще еще можно просто сделать

for i in {00001..99999}; do
  echo $i
done

16
Это работает в Bash версии 4.1.2 (CentOS 6), но не работает в версии 3.2.25 (CentOS 5). Я согласен, что это гораздо более эстетичный способ сделать это!
Майк Старов

1
Работает, но не дает заполненных нулями строк на моем bash
user2707001

На Mac (Bash 4.4) это не дополняет цифры. На CentOS & SuSE это прекрасно работает!
Стефан Ласевский

Это хорошо работает на macOS с Bash4.4.23(1)-release
Whymarrh

К сожалению, не работает на Bash 5 на OS X
gotofritz

91

Если конец последовательности имеет максимальную длину заполнения (например, если вы хотите 5 цифр и команда имеет значение «seq 1 10000»), то вы можете использовать флаг «-w» для seq - он добавляет сам заполнитель.

seq -w 1 10

производить

01
02
03
04
05
06
07
08
09
10

7
Это, очевидно, правильный ответ. Почему у него так мало голосов? Ad
adius

5
легко, потому что заполнение зависит от максимального числа, которое вы хотите достичь. Если это параметр, вы никогда не знаете заранее, сколько нулей вы получите
guillem

5
Это не дает заданную фиксированную длину, просто результаты, которые имеют одинаковую длину.
Ceisc



12

Используйте awk следующим образом:

awk -v start=1 -v end=10 'BEGIN{for (i=start; i<=end; i++) printf("%05d\n", i)}'

ВЫВОД:

00001
00002
00003
00004
00005
00006
00007
00008
00009
00010

Обновить:

В качестве чистой альтернативы bash вы можете сделать это, чтобы получить тот же результат:

for i in {1..10}
do
   printf "%05d\n" $i
done

Таким образом, вы можете избежать использования внешней программы, seqкоторая НЕ доступна для всех разновидностей * nix.


1
Мы можем использовать awk's BEGINоператор так, чтобы нам не приходилось использовать heredocприсвоение.
Джайпал Сингх

@JaypalSingh: Спасибо, приятель, это хороший момент. Обновил мой ответ.
анубхава

9

Я дополняю вывод большим количеством цифр (нулей), чем мне нужно, затем использую tail, чтобы использовать только то количество цифр, которое я ищу. Обратите внимание, что вы должны использовать «6» в хвосте, чтобы получить последние пять цифр :)

for i in $(seq 1 10)
do
RESULT=$(echo 00000$i | tail -c 6)
echo $RESULT
done

Если вы хотите использовать правильное количество мест в аргументе -c хвоста, вы можете использовать 'echo -n 00000 $ i', так как это останавливает вывод новой строки, возвращаемой одним из символов, возвращаемым хвостом, следовательно, ему нужно быть на один уровень выше в этом ответе. В зависимости от того, что вы делаете, символ новой строки, если он останется, может повлиять на результаты.
Ceisc

Использование внешнего процесса для обрезки переменной в цикле довольно неэффективно. Вместо этого вы можете использовать функции расширения параметров оболочки . В Bash есть средство для извлечения конкретной подстроки по индексу, или вы можете использовать подстановку префикса и суффикса с шаблоном, который соответствует желаемой ширине, хотя это требует немного вперед-назад.
tripleee


2

Это будет работать также:

for i in {0..9}{0..9}{0..9}{0..9}
do
  echo "$i"
done

Отличное решение! Чистый, легко читаемый, никаких дополнительных программ не требуется.
Джонатан Кросс

2

Другой путь :

zeroos="000"
echo 

for num in {99..105};do
 echo ${zeroos:${#num}:${#zeroos}}${num}
done

Так что простая функция для преобразования любого числа будет:

function leading_zero(){

    local num=$1
    local zeroos=00000
    echo ${zeroos:${#num}:${#zeroos}}${num} 

}

1

Один из способов без использования внешнего процесса - это манипулирование строками, в общем случае это будет выглядеть так:

#start value
CNT=1

for [whatever iterative loop, seq, cat, find...];do
   # number of 0s is at least the amount of decimals needed, simple concatenation
   TEMP="000000$CNT"
   # for example 6 digits zero padded, get the last 6 character of the string
   echo ${TEMP:(-6)}
   # increment, if the for loop doesn't provide the number directly
   TEMP=$(( TEMP + 1 ))
done

Это хорошо работает и на WSL, где разветвление - очень тяжелая операция. У меня был список 110000 файлов, использование printf "%06d" $NUMзаняло более 1 минуты, решение выше заняло около 1 секунды.


0

1.) Создайте последовательность чисел 'seq' от 1 до 1000 и зафиксируйте ширину '-w' (ширина определяется длиной конечного числа, в данном случае 4 цифры на 1000).

2.) Кроме того, выберите, какие цифры вы хотите, используя «sed -n» (в этом случае мы выбираем номера 1-100).

3.) «эхо» каждого номера. Числа хранятся в переменной «i», доступ к которой осуществляется с помощью «$».

Плюсы: этот код довольно чистый.

Минусы: seq не свойственен всем системам Linux (насколько я понимаю)

for i in `seq -w 1 1000 | sed -n '1,100p'`; 
do 
    echo $i; 
done

0

Если вы просто добавляете числа с нулями для достижения фиксированной длины, просто добавьте ближайшее кратное 10, например. для 2 цифр добавьте 10 ^ 2, затем удалите первую 1 перед отображением вывода.

Это решение работает для заполнения / форматирования отдельных чисел любой длины или целой последовательности чисел с использованием цикла for.

# Padding 0s zeros:
# Pure bash without externals eg. awk, sed, seq, head, tail etc.
# works with echo, no need for printf

pad=100000      ;# 5 digit fixed

for i in {0..99999}; do ((j=pad+i))
    echo ${j#?}
done

Протестировано на Mac OSX 10.6.8, Bash ver 3.2.48

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.