Jak zerować pad liczb całkowitych w bash, aby wszystkie miały tę samą szerokość?


426

Muszę zapętlić niektóre wartości,

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

Dla $firsti $last, muszę go mieć ustaloną długość 5. Więc jeśli wejście jest 1, muszę dodać zer w przód tak, że staje się 00001. Zapętla się, 99999na przykład, ale długość musi wynosić 5.

Np: 00002, 00042, 00212, 012312i tak dalej.

Masz pomysł, jak to zrobić?


22
Dobrą odpowiedzią może być: seq - w 1 99999. Opcja -w utrzymuje stałą długość wyjściową, uzupełniając najkrótsze liczby cyfrą 0.
Bruno von Paris


Wiele odpowiedzi tutaj zaleca, for variable in $(something to generate the numbers); do ...ale jest to problematyczne, gdy lista liczb jest długa. Jest o wiele bardziej wydajny w użyciu something to generate the numbers | while read -r variable; do .... Zobacz także mywiki.wooledge.org/DontReadLinesWithFor, w którym omawia się czytanie wierszy z plików itp., Ale niektóre argumenty również tutaj mają zastosowanie.
tripleee

Odpowiedzi:


710

Jednak w twoim konkretnym przypadku prawdopodobnie najłatwiej jest użyć -fflagi seq, aby sformatować liczby podczas wyświetlania listy. Na przykład:

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

wytworzy następujące dane wyjściowe:

00010
00011
00012
00013
00014
00015

Mówiąc bardziej ogólnie, bashma printfwbudowaną funkcję, dzięki czemu można uzupełniać dane wyjściowe zerami w następujący sposób:

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

Możesz użyć -vflagi do przechowywania danych wyjściowych w innej zmiennej:

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

Zauważ, że printfobsługuje nieco inny format, seqwięc musisz użyć %05dzamiast %05g.


5
%05gzamiast %05dtego dla mnie naprawić. „00026” dawało mi „00022” . Dzięki!
Ed Manet

12
Świetna, kompleksowa odpowiedź. Jedna mała korekta: ściśle mówiąc, seqobsługuje podzbiór znaków formatu. który printfobsługuje (i który obejmuje podzbiory g, ale nie obejmuje d). Zachowanie znaków di gróżni się subtelnie tym, że dinterpretuje 0ciągi liczb z prefiksem jako liczby ósemkowe i konwertuje je na dziesiętne, podczas gdy gtraktuje je jako dziesiętne. (@EdManet: dlatego „00026” zamieniło się w „00022” z d). Kolejna rzecz, o której warto wspomnieć: seq -wczy automatyczne uzupełnianie zerami liczb wyjściowych na podstawie największej liczby wygenerowanych.
mklement0

Pomogło mi to wygenerować listę znaków szesnastkowych. for (( i = 0; i <= 0xffffffff; i++ )) do printf "%08x\n" $i ; done >> hex.txtutworzył 8-znakową listę szesnastkową. Dzięki.
cde

seq --help: „FORMAT musi nadawać się do drukowania jednego argumentu typu„ double ”; domyślnie jest to% .PRECf, jeśli FIRST, INCREMENT i LAST są liczbami stałymi dziesiętnymi z maksymalną dokładnością PREC, a w przeciwnym razie% g.” Możliwe specyfikatory konwersji to efgaEFGA.
x-yuri

133

Łatwiej jeszcze możesz to zrobić

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

16
Działa to w wersji Bash 4.1.2 (CentOS 6), ale nie działa w wersji 3.2.25 (CentOS 5). Zgadzam się, że jest to o wiele bardziej estetyczny sposób!
Mike Starov

1
Działa, ale nie daje zerowanych łańcuchów na mojej bashu
user2707001

Na komputerze Mac (Bash 4.4) nie wpisuje się liczb. Na CentOS i SuSE działa świetnie!
Stefan Lasiewski

Działa to dobrze na macOS z Bash4.4.23(1)-release
Whymarrh

Niestety nie działa również na Bash 5 w systemie OS X
gotofritz

91

Jeśli koniec sekwencji ma maksymalną długość dopełnienia (na przykład, jeśli chcesz 5 cyfr, a polecenie to „seq 1 10000”), możesz użyć flagi „-w” dla seq - dodaje to dopełnienie.

seq -w 1 10

produkować

01
02
03
04
05
06
07
08
09
10

7
To jest oczywiście poprawna odpowiedź. Dlaczego ma tak mało pozytywnych opinii? 😳
adius

5
łatwe, ponieważ wypełnienie zależy od maksymalnej liczby, którą chcesz osiągnąć. Jeśli jest to parametr, którego nigdy wcześniej nie wiesz, ile zera skończysz
guillem

5
Nie daje to określonej stałej długości, a jedynie wyniki o tej samej długości.
Ceisc


18

Bardzo proste w użyciu printf

[jaypal:~/Temp] printf "%05d\n" 1
00001
[jaypal:~/Temp] printf "%05d\n" 2
00002

12

Użyj awk w następujący sposób:

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

WYNIK:

00001
00002
00003
00004
00005
00006
00007
00008
00009
00010

Aktualizacja:

Jako czystą alternatywę bash możesz to zrobić, aby uzyskać ten sam wynik:

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

W ten sposób można uniknąć używania zewnętrznego programu, seqktóry NIE jest dostępny we wszystkich wersjach * nix.


1
Możemy użyć awk's BEGINoświadczenia, abyśmy nie musieli korzystać z heredoczadania.
jaypal singh

@JaypalSingh: Dzięki kolego, to dobra uwaga. Zaktualizowałem moją odpowiedź.
anubhava

9

Wypełniam dane wyjściowe większą liczbą cyfr (zer) niż potrzebuję, a następnie używam tail, aby użyć tylko liczby cyfr, których szukam. Zauważ, że musisz użyć „6” w ogonie, aby uzyskać ostatnie pięć cyfr :)

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

Jeśli chcesz użyć poprawnej liczby miejsc w argumencie -c argumentu tail, możesz użyć 'echo -n 00000 $ i', ponieważ zatrzymuje to wypisywanie nowego wiersza, który jest jednym z znaków, który zwraca ogon, dlatego musi bądź wyżej w tej odpowiedzi. W zależności od tego, co robisz, nowa linia, jeśli zostanie pozostawiona, może wpłynąć na wyniki.
Ceisc

Użycie zewnętrznego procesu do przycięcia zmiennej w pętli jest raczej nieefektywne. Zamiast tego możesz użyć funkcji rozszerzania parametrów powłoki . W Bash istnieje możliwość wyodrębnienia określonego podłańcucha za pomocą indeksu, lub możesz użyć podstawienia przedrostka i sufiksu ze wzorem pasującym do pożądanej szerokości, choć wymaga to nieco tam iz powrotem.
tripleee

4

Jeśli chcesz N cyfr, dodaj 10 ^ N i usuń pierwszą cyfrę.

for (( num=100; num<=105; num++ ))
do
  echo ${num:1:3}
done

Wynik:

01
02
03
04
05

2

Działa to również:

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

Fajne rozwiązanie! Czysty, łatwy do odczytania, bez dodatkowych programów.
Jonathan Cross

2

Inny sposób :

zeroos="000"
echo 

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

Tak prostą funkcją konwersji dowolnej liczby byłoby:

function leading_zero(){

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

}

1

Jednym ze sposobów bez użycia zewnętrznego rozwidlania procesów jest manipulacja ciągami, w ogólnym przypadku wyglądałoby to tak:

#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

Działa to również całkiem dobrze na WSL, gdzie rozwidlenie jest naprawdę ciężką operacją. Miałem listę 110000 plików, użycie printf "%06d" $NUMzajęło mi ponad 1 minutę, powyższe rozwiązanie działało za około 1 sekundę.


0

1.) Utwórz ciąg liczb „seq” od 1 do 1000 i ustal szerokość „-w” (szerokość jest określana na podstawie długości numeru końcowego, w tym przypadku 4 cyfry na 1000).

2.) Wybierz także liczby, które chcesz, używając „sed -n” (w tym przypadku wybieramy liczby 1-100).

3.) „echo” każdego numeru. Liczby są przechowywane w zmiennej „i”, dostępnej za pomocą „$”.

Plusy: Ten kod jest całkiem czysty.

Wady: „seq” nie jest natywny dla wszystkich systemów Linux (jak rozumiem)

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

0

Jeśli chcesz dopełnić liczby zerami, aby uzyskać stałą długość, po prostu dodaj najbliższą wielokrotność 10, np. dla 2 cyfr dodaj 10 ^ 2, a następnie usuń pierwszą 1 przed wyświetleniem wyniku.

To rozwiązanie działa na pad / format pojedynczych liczb o dowolnej długości lub całej sekwencji liczb za pomocą pętli 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

Testowane na Mac OSX 10.6.8, Bash wersja 3.2.48

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.