Làm thế nào để zero pad một chuỗi các số nguyên trong bash để tất cả có cùng chiều rộng?


426

Tôi cần lặp một số giá trị,

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

Đối với $first$last, tôi cần nó có độ dài cố định 5. Vì vậy, nếu đầu vào là 1, tôi cần thêm các số 0 ở phía trước để nó trở thành 00001. Nó vòng cho đến 99999ví dụ, nhưng độ dài phải là 5.

Ví dụ như: 00002, 00042, 00212, 012312và vân vân.

Bất cứ ý tưởng về làm thế nào tôi có thể làm điều đó?


22
Một câu trả lời hay có thể là: seq - w 1 99999. Tùy chọn -w giữ đầu ra chiều dài không đổi, đệm các số ngắn nhất bằng 0.
Bruno von Paris


Nhiều câu trả lời ở đây khuyến nghị for variable in $(something to generate the numbers); do ...nhưng đây là vấn đề khi danh sách các số dài. Nó là hiệu quả hơn nhiều để sử dụng something to generate the numbers | while read -r variable; do .... Xem thêm mywiki.wooledge.org/DontReadLinesWithFor thảo luận về việc đọc các dòng từ các tệp, v.v., nhưng một số đối số cũng áp dụng ở đây.
tripleee

Câu trả lời:


710

Trong trường hợp cụ thể của bạn, mặc dù có thể sử dụng -fcờ dễ nhất seqđể định dạng các số khi nó xuất ra danh sách. Ví dụ:

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

sẽ tạo ra đầu ra sau:

00010
00011
00012
00013
00014
00015

Nói chung, bashcó sẵn printfnhư là một tích hợp để bạn có thể đệm đầu ra với số không như sau:

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

Bạn có thể sử dụng -vcờ để lưu trữ đầu ra trong một biến khác:

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

Lưu ý rằng printfhỗ trợ một định dạng hơi khác để seqbạn cần sử dụng %05dthay vì %05g.


5
%05gthay vì %05dsửa nó cho tôi "00026" đã cho tôi "00022" . Cảm ơn!
Ed Manet

12
Tuyệt vời, câu trả lời toàn diện. Một điều chỉnh nhỏ: nói đúng, seqhỗ trợ một tập hợp các ký tự định dạng. rằng printfsự ủng hộ (và các tập con trong đó bao gồm g, nhưng không phải d). Hành vi của các ký tự dgkhác nhau một cách tinh tế trong đó ddiễn giải 0các chuỗi số được tổng hợp thành các số bát phân và chuyển đổi chúng thành số thập phân, trong khi gcoi chúng là số thập phân. (@EdManet: đó là lý do tại sao '00026' biến thành '00022' với d). Một điều đáng nói nữa: seq -wkhông tự động đệm số của các số đầu ra dựa trên số rộng nhất được tạo.
mkuity0

Điều này giúp tôi tạo danh sách các ký tự hex. for (( i = 0; i <= 0xffffffff; i++ )) do printf "%08x\n" $i ; done >> hex.txttạo ra một danh sách thập lục phân 8 ký tự. Cảm ơn.
cde

seq --help: "FORMAT phải phù hợp để in một đối số loại 'double'; nó mặc định là% .PRECf nếu FIRST, INCREMENT và LAST đều là các số thập phân điểm cố định với PREC chính xác tối đa và khác với% g." Các chỉ định chuyển đổi có thể là efgaEFGA.
x-yuri

133

Vẫn dễ dàng hơn bạn chỉ có thể làm

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

16
Điều này hoạt động trên Bash phiên bản 4.1.2 (CentOS 6) nhưng không thành công trong phiên bản 3.2.25 (CentOS 5). Tôi đồng ý rằng đây là cách làm thẩm mỹ hơn nhiều để làm điều đó!
Mike Starov

1
Hoạt động, nhưng không cung cấp chuỗi đệm bằng 0 trên bash của tôi
user2707001

Trên máy Mac (Bash 4.4), điều này không đệm số. Trên CentOS & SuSE, nó hoạt động rất tốt!
Stefan Lasiewski

Điều này hoạt động tốt trên macOS với Bash4.4.23(1)-release
Whymarrh


91

Nếu phần cuối của chuỗi có độ dài tối đa của phần đệm (ví dụ: nếu bạn muốn có 5 chữ số và lệnh là "seq 1 10000"), thì bạn có thể sử dụng cờ "-w" cho seq - nó tự thêm phần đệm.

seq -w 1 10

sản xuất

01
02
03
04
05
06
07
08
09
10

7
Đây rõ ràng là câu trả lời chính xác. Tại sao nó có quá ít upvote? 😳
adius

5
dễ dàng, bởi vì phần đệm phụ thuộc vào số lượng tối đa bạn muốn đạt được. Nếu đây là thông số bạn không bao giờ biết trước thì bạn sẽ có bao nhiêu số không
guillem

5
Điều này không đưa ra một độ dài cố định cụ thể, chỉ là các kết quả có cùng độ dài.
Ceisc

78

sử dụng printf với "% 05d", vd

printf "%05d" 1

18

Sử dụng rất đơn giản printf

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

12

Sử dụng awk như thế này:

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

ĐẦU RA:

00001
00002
00003
00004
00005
00006
00007
00008
00009
00010

Cập nhật:

Là phương án bash thuần túy, bạn có thể làm điều này để có cùng một đầu ra:

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

Bằng cách này bạn có thể tránh sử dụng một chương trình bên ngoài seqmà là KHÔNG có sẵn trên tất cả các hương vị của * nix.


1
Chúng ta có thể sử dụng awk's BEGINcâu lệnh để không phải sử dụng heredocphép gán.
jaypal singh

@JaypalSingh: Cảm ơn bạn, đó là một điểm tốt. Cập nhật câu trả lời của tôi.
anubhava

9

Tôi đệm đầu ra với nhiều chữ số (số không) hơn tôi cần sau đó sử dụng đuôi để chỉ sử dụng số chữ số tôi đang tìm kiếm. Lưu ý rằng bạn phải sử dụng đuôi '6' để có được năm chữ số cuối :)

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

Nếu bạn muốn sử dụng đúng số lượng địa điểm trong đối số -c của đuôi, bạn có thể sử dụng 'echo -n 00000 $ i' vì điều này ngăn nó xuất ra một dòng mới là một trong những đuôi chars đang quay trở lại, do đó nó cần phải là một cao hơn trong câu trả lời này. Tùy thuộc vào những gì bạn đang làm, dòng mới, nếu còn lại, có thể ảnh hưởng đến kết quả.
Ceisc

Sử dụng một quy trình bên ngoài để cắt biến trong vòng lặp là không hiệu quả. Bạn có thể sử dụng các tính năng mở rộng tham số của shell thay thế. Trong Bash có một phương tiện để trích xuất một chuỗi con cụ thể theo chỉ mục hoặc bạn có thể sử dụng các thay thế tiền tố và hậu tố với một mẫu phù hợp với chiều rộng mong muốn, mặc dù nó đòi hỏi một chút qua lại.
tripleee

4

Nếu bạn muốn N chữ số, thêm 10 ^ N và xóa chữ số đầu tiên.

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

Đầu ra:

01
02
03
04
05

2

Điều này cũng sẽ làm việc:

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

Giải pháp tốt đẹp! Sạch sẽ, dễ đọc, không cần chương trình bổ sung.
Jonathan Cross

2

Cách khác :

zeroos="000"
echo 

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

Vì vậy, chức năng đơn giản để chuyển đổi bất kỳ số nào sẽ là:

function leading_zero(){

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

}

0

1.) Tạo một chuỗi các số 'seq' từ 1 đến 1000 và sửa chiều rộng '-w' (chiều rộng được xác định theo độ dài của số kết thúc, trong trường hợp này là 4 chữ số cho 1000).

2.) Ngoài ra, chọn số bạn muốn sử dụng 'sed -n' (trong trường hợp này, chúng tôi chọn số 1-100).

3.) 'echo' ra mỗi số. Các số được lưu trữ trong biến 'i', được truy cập bằng cách sử dụng '$'.

Ưu điểm: Mã này khá sạch.

Nhược điểm: 'seq' không có nguồn gốc từ tất cả các hệ thống Linux (theo tôi hiểu)

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

0

Nếu bạn chỉ sau khi đệm số có số không để đạt được độ dài cố định, chỉ cần thêm bội số gần nhất của 10, vd. cho 2 chữ số, thêm 10 ^ 2, sau đó xóa 1 đầu tiên trước khi hiển thị đầu ra.

Giải pháp này hoạt động để đệm / định dạng các số đơn có độ dài bất kỳ hoặc toàn bộ chuỗi số sử dụng vòng lặp 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

Đã thử nghiệm trên Mac OSX 10.6.8, Bash ver 3.2.48


0

Một cách mà không sử dụng quá trình giả mạo bên ngoài là thao tác chuỗi, trong trường hợp chung, nó sẽ trông như thế này:

#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

Điều này cũng hoạt động khá tốt trên WSL, trong đó forking là một hoạt động thực sự nặng nề. Tôi đã có một danh sách 110000 tệp, sử dụng printf "%06d" $NUMmất hơn 1 phút, giải pháp ở trên chạy trong khoảng 1 giây.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.