Viết một ký tự N lần bằng lệnh printf


12

Tôi đã tìm thấy lệnh sau để lặp lại một ký tự trong Linux:

printf 'H%.0s' {1..5000} > H.txt

Tôi muốn, ví dụ, Hđể lặp lại 5000lần. Không gì %.0scó nghĩa là ở đây?


Với tcshhoặc zsh, repeat 5000 printf Hdễ hiểu hơn. Với perl: print "H" x 5000(lưu ý rằng {1..5000}là một nhà điều hành zsh lấy cảm hứng từ perl's 1..5000một và sau đó sao chép bởi ksh93 và bash)
Stéphane Chazelas

vâng, nó hoạt động, nhưng sử dụng nhiều tài nguyên để lặp lại lớn hơn, hãy làm theo các đề xuất của Stéphane Chazelas
Skaperen

1
tôi sẽ thực hiện lệnh nàyyes H|head -5000|tr -d '\012'
Skaperen 7/03/2015

dd if=/dev/zero bs=5000 count=1 | tr '\0' H
kojiro 7/03/2015

@Skaepren:yes H| head -n 2500| tr \\n H
mikeerv

Câu trả lời:


20

Lệnh đó phụ thuộc vào trình bao tạo 5000 đối số và chuyển chúng tới printfđó bỏ qua chúng. Mặc dù nó có vẻ khá nhanh - và có liên quan đến một số thứ - shell vẫn phải tạo ra tất cả các chuỗi đó dưới dạng đối số (và phân định chúng) , v.v.

Bên cạnh thực tế là các Hs được tạo không thể được in cho đến khi trình bao đầu tiên lặp lại thành 5000, lệnh đó cũng tiêu tốn trong bộ nhớ tất cả những gì nó cần để lưu trữ và phân định các đối số chuỗi số để printf cộng với Hs. Chỉ đơn giản là bạn có thể làm:

printf %05000s|tr \  H

... tạo ra một chuỗi 5000 khoảng trắng - mà, ít nhất, thường chỉ là một byte cho mỗi và không có gì để phân định vì chúng không được phân định. Một vài thử nghiệm chỉ ra rằng thậm chí chỉ với 5000 byte, chi phí của ngã ba và đường ống cần thiết trlà đáng giá ngay cả trong trường hợp này, và hầu như luôn luôn là khi con số tăng cao hơn.

Tôi đã chạy ...

time bash -c 'printf H%.0s {1..5000}' >/dev/null

... và ...

time bash -c 'printf %05000s|tr \  H' >/dev/null

Mỗi lần khoảng 5 lần một mảnh (không có gì khoa học ở đây - chỉ là giai thoại) và phiên bản mở rộng niềng trung bình hơn 0,02 giây trong tổng thời gian xử lý, nhưng phiên bản trung bình đạt trkhoảng 0,012 giây - và trphiên bản đã đánh bại nó mỗi lần. Tôi không thể nói rằng tôi ngạc nhiên - {brace expansion}là một tính năng tốc ký tương tác hữu ích, nhưng thường là một điều khá lãng phí khi làm bất kỳ loại kịch bản nào có liên quan. Hình thức phổ biến:

for i in {[num]..[num]}; do ...

... Khi bạn nghĩ về nó, thực sự là hai for vòng lặp - đầu tiên là bên trong và ngụ ý rằng shell phải lặp theo một cách nào đó để tạo ra các vòng lặp đó trước khi lưu tất cả chúng và lặp lại chúng cho forvòng lặp của bạn . Những việc như vậy thường được thực hiện tốt hơn như:

iterator=$start
until [ "$((iterator+=interval))" -gt "$end" ]; do ...

... bởi vì bạn chỉ lưu trữ một vài giá trị và ghi đè lên chúng khi bạn thực hiện cũng như thực hiện phép lặp trong khi bạn tạo các lần lặp.

Dù sao, giống như phần đệm không gian được đề cập trước đó, bạn cũng có thể sử dụng printfđể zeropad một số chữ số tùy ý, tất nhiên, như:

printf %05000d

Tôi làm cả hai mà không có đối số bởi vì đối với mọi đối số được chỉ định trong printfchuỗi định dạng của khi không tìm thấy đối số, chuỗi null được sử dụng - được hiểu là số không cho đối số chữ số hoặc chuỗi trống cho chuỗi.

Đây là mặt khác (và - theo ý kiến ​​của tôi - hiệu quả hơn) của đồng xu khi so sánh với lệnh trong câu hỏi - trong khi có thể không nhận được gì từ một thứ gì đó như bạn làm khi bạn printf %.0kéo dài chuỗi cho mỗi đối số, do đó, cũng vậy nó có thể nhận được một cái gì đó từ không có gì.

Vẫn nhanh hơn cho số lượng lớn byte được tạo mà bạn có thể sử dụng ddnhư:

printf \\0| dd bs=64k conv=sync 

... và đối số ddcủa các tệp thông thường seek=[num]có thể được sử dụng để tạo lợi thế lớn hơn. Bạn có thể nhận 64k dòng mới chứ không phải là null nếu bạn thêm ,unblock cbs=1vào bên trên và từ đó có thể tiêm chuỗi tùy ý mỗi dòng với paste/dev/null- nhưng trong trường hợp đó, nếu nó có sẵn cho bạn, bạn cũng có thể sử dụng:

yes 'output string forever'

Dưới đây là một số ddví dụ nữa:

dd bs=5000 seek=1 if=/dev/null of=./H.txt

... tạo ra (hoặc cắt bớt) một \0NULtệp được điền trong thư mục hiện tại có tên H.txt có kích thước 5000 byte. ddtìm kiếm thẳng đến phần bù và NUL-lấp đầy tất cả đằng sau nó.

<&1 dd bs=5000 conv=sync,noerror count=1 | tr \\0 H >./H.txt

... tạo ra một tệp có cùng tên và kích thước nhưng được điền vào ký tự w / H. Nó lợi dụng ddhành vi cụ thể của việc viết ra ít nhất một khối null hoàn toàn trong trường hợp xảy ra lỗi đọc noerrorsyncchuyển đổi được chỉ định (và - không count=- có thể sẽ kéo dài hơn bạn muốn) và chuyển hướng có chủ ý một mô tả tập tin writeonly tại ddstdin.


8

Các %.0sphương tiện để chuyển đổi đối số dưới dạng một chuỗi , với độ chính xác bằng không. Theo man 3 printf, giá trị chính xác trong trường hợp như vậy mang lại

   [ ... ] the  maximum  number  of characters to be printed from a
   string for s and S conversions.

do đó khi độ chính xác bằng 0, đối số chuỗi hoàn toàn không được in. Tuy nhiên, H(là một phần của trình xác định định dạng) được in nhiều lần khi có các đối số, vì theo printfphần củaman bash

The format is reused as necessary to consume all  of  the  argu
ments.  If the format requires more arguments than are supplied,
the extra format specifications behave as if  a  zero  value  or
null  string,  as  appropriate,  had  been supplied. 

7

Trong trường hợp này, %.0sluôn in một trường hợp ký tự đứng trước nó, H trong trường hợp này. Khi bạn sử dụng {1..5000}, trình bao mở rộng nó và nó trở thành:

printf 'H%.0s' 1 2 3 4 ... 5000 > H.txt

tức là, lệnh printf hiện có 5000 đối số và với mỗi đối số, bạn sẽ nhận được một H. Chúng không phải là tuần tự hoặc số:

printf 'H%.0s' a bc fg 12 34

in HHHHH- tức là số lượng đối số, 5 trong trường hợp này.

Lưu ý, các hình elip trong ví dụ đầu tiên ở trên không được chèn theo nghĩa đen, chúng ở đó để chỉ ra một chuỗi hoặc phạm vi.

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.