Menulis karakter N kali menggunakan perintah printf

12

Saya menemukan perintah berikut untuk mengulangi karakter di Linux:

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

Saya ingin, misalnya, Hmengulangi 5000waktu. Apa yang dimaksud di %.0ssini?

bintang
sumber
Dengan tcshatau zsh, repeat 5000 printf Hlebih mudah dimengerti. Dengan perl: print "H" x 5000(catatan bahwa {1..5000}adalah operator zsh terinspirasi oleh perl's 1..5000satu dan kemudian disalin oleh ksh93 dan bash)
Stéphane Chazelas
ya itu berhasil, tetapi menggunakan banyak sumber daya untuk pengulangan yang lebih besar, ikuti saran oleh Stéphane Chazelas
Skaperen
1
saya akan melakukan perintah iniyes H|head -5000|tr -d '\012'
Skaperen
dd if=/dev/zero bs=5000 count=1 | tr '\0' H
kojiro
@Sepepren:yes H| head -n 2500| tr \\n H
mikeserv

Jawaban:

20

Perintah itu tergantung pada shell yang menghasilkan 5000 argumen, dan meneruskannya printfyang kemudian mengabaikannya. Meskipun mungkin terlihat cukup cepat - dan relatif untuk beberapa hal - shell masih harus menghasilkan semua string sebagai args (dan membatasi mereka) dan seterusnya.

Selain fakta bahwa Hs yang dihasilkan tidak dapat dicetak sampai shell pertama kali beralih ke 5000, perintah itu juga menghabiskan semua memori yang diperlukan untuk menyimpan dan membatasi argumen string numerik menjadi printf ditambah Hs. Sederhananya yang dapat Anda lakukan:

printf %05000s|tr \  H

... yang menghasilkan string 5000 ruang - yang, setidaknya, biasanya hanya satu byte per dan tidak ada biaya untuk membatasi karena tidak dibatasi. Beberapa tes menunjukkan bahwa bahkan untuk sedikitnya 5.000 byte biaya garpu dan pipa yang diperlukan untuk tritu layak bahkan dalam kasus ini, dan hampir selalu adalah ketika jumlahnya semakin tinggi.

Saya berlari ...

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

...dan...

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

Masing-masing sekitar 5 kali sepotong (tidak ada yang ilmiah di sini - hanya anekdotal) dan versi ekspansi brace rata-rata sedikit lebih dari 0,02 detik dalam total waktu pemrosesan, tetapi trversi datang sekitar rata-rata total 0,012 detik - dan trversi mengalahkannya setiap saat. Saya tidak bisa mengatakan saya terkejut - {brace expansion}adalah fitur steno shell interaktif yang berguna, tetapi biasanya hal yang agak boros untuk dilakukan di mana pun jenis scripting yang bersangkutan. Bentuk umum:

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

... ketika Anda memikirkannya, benar-benar dua for loop - yang pertama adalah internal dan tersirat dalam bahwa shell harus diulang dengan cara tertentu untuk menghasilkan iterator sebelum menyimpan semuanya dan mengulanginya lagi untuk forloop Anda . Hal-hal seperti itu biasanya lebih baik dilakukan seperti:

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

... karena Anda menyimpan nilai yang sangat sedikit dan menimpa mereka saat Anda pergi serta melakukan iterasi saat Anda menghasilkan iterables.

Pokoknya, seperti ruang yang disebutkan sebelumnya, Anda juga dapat menggunakan printfuntuk zeropad angka yang berubah-ubah, tentu saja, seperti:

printf %05000d

Saya melakukan keduanya tanpa argumen karena untuk setiap argumen yang ditentukan dalam printfformat string ketika sebuah argumen tidak ditemukan, string nol digunakan - yang ditafsirkan sebagai nol untuk argumen digit atau string kosong untuk string.

Ini adalah sisi lain (dan - menurut saya - lebih efisien) dari sisi koin jika dibandingkan dengan perintah dalam pertanyaan - sementara dimungkinkan untuk tidak mendapatkan apa pun dari sesuatu seperti yang Anda lakukan saat Anda printf %.0memanjang string untuk setiap argumen, demikian juga mungkin untuk mendapatkan sesuatu dari ketiadaan.

Masih lebih cepat untuk sejumlah besar byte yang dihasilkan yang dapat Anda gunakan ddseperti:

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

... dan dddengan seek=[num]argumen file biasa dapat digunakan untuk keuntungan yang lebih besar. Anda bisa mendapatkan 64k baris baru daripada nol jika Anda menambahkan ,unblock cbs=1di atas dan dari sana bisa menyuntikkan string sewenang-wenang per baris dengan pastedan /dev/null- tetapi dalam hal itu, jika tersedia untuk Anda, Anda mungkin sebaiknya menggunakan:

yes 'output string forever'

Berikut ini beberapa ddcontoh lagi:

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

... yang menciptakan (atau memotong) sebuah \0NULfile yang diisi dengan direktori saat ini bernama H.txt ukuran 5000 bytes. ddmencari langsung ke offset dan NUL-mengisi semua di belakangnya.

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

... yang membuat file dengan nama dan ukuran yang sama tetapi diisi dengan karakter w. Ini mengambil keuntungan dari ddperilaku spesifik menulis setidaknya satu nol blok penuh jika terjadi kesalahan baca ketika noerrordan synckonversi ditentukan (dan - tanpa count=- kemungkinan akan berlangsung lebih lama dari yang Anda inginkan) , dan dengan sengaja mengarahkan ulang deskriptor file writeonly dddi stdin.

mikeserv
sumber
8

The %.0sberarti untuk mengkonversi argumen sebagai tali , dengan presisi dari nol. Menurut man 3 printf, nilai presisi dalam kasus seperti itu memberi

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

karenanya ketika presisi adalah nol, argumen string tidak dicetak sama sekali. Namun H(yang merupakan bagian dari penentu format) akan dicetak sebanyak yang ada argumen, karena sesuai dengan printfbagianman 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. 
Steeldriver
sumber
7

Dalam hal ini, %.0sselalu cetak satu instance karakter sebelumnya, H dalam hal ini. Saat Anda menggunakan {1..5000}, shell memperluasnya dan itu menjadi:

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

yaitu, perintah printf sekarang memiliki 5000 argumen, dan untuk setiap argumen, Anda akan mendapatkan satu H. Ini tidak harus berurutan atau numerik:

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

cetakan HHHHH- yaitu, jumlah argumen, 5 dalam hal ini.

Catatan, elips pada contoh 1 di atas tidak dimasukkan secara harfiah, mereka ada di sana untuk menunjukkan urutan atau rentang.

KM.
sumber