การเขียนอักขระ N ครั้งโดยใช้คำสั่ง printf


12

ฉันพบคำสั่งต่อไปนี้เพื่อทำซ้ำอักขระใน Linux:

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

ตัวอย่างเช่นฉันต้องการที่Hจะทำซ้ำ5000ครั้ง ที่%.0sนี่หมายความว่าอย่างไร


ด้วยtcshหรือzsh, repeat 5000 printf Hง่ายต่อการเข้าใจ ด้วยperl: print "H" x 5000(โปรดทราบว่านั่น{1..5000}คือตัวดำเนินการ zsh ที่ได้รับแรงบันดาลใจจากperl's 1..5000และคัดลอกมาโดย ksh93 และ bash)
Stéphane Chazelas

ใช่มันใช้งานได้ แต่ใช้ทรัพยากรจำนวนมากเพื่อทำซ้ำให้ใหญ่ขึ้นปฏิบัติตามคำแนะนำของStéphane Chazelas
Skaperen

1
ฉันจะทำคำสั่งนี้yes H|head -5000|tr -d '\012'
Skaperen

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

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

คำตอบ:


20

คำสั่งนั้นขึ้นอยู่กับเชลล์ที่สร้างอาร์กิวเมนต์จำนวน 5,000 ข้อและส่งต่อไปยังprintfสิ่งที่ไม่สนใจ ในขณะที่มันอาจดูค่อนข้างเร็ว - และสัมพันธ์กับบางสิ่ง - เชลล์จะต้องสร้างสตริงเหล่านั้นทั้งหมดเป็น args (และกำหนดขอบเขต)และอื่น ๆ

นอกเหนือจากข้อเท็จจริงที่ว่า Hs ที่สร้างขึ้นไม่สามารถพิมพ์ได้จนกว่าเชลล์จะทำซ้ำถึง 5,000 ครั้งแรกคำสั่งนั้นจะมีค่าใช้จ่ายในหน่วยความจำทั้งหมดที่ใช้ในการจัดเก็บและกำหนดขอบเขตอาร์กิวเมนต์สตริงตัวเลขให้เป็นprintf บวกกับ Hs เช่นเดียวกับที่คุณสามารถทำได้:

printf %05000s|tr \  H

... ซึ่งสร้างสตริงของช่องว่าง 5000 - ซึ่งอย่างน้อยที่สุดจะมีเพียงไบต์เดียวต่อหนึ่งและไม่มีค่าใช้จ่ายใด ๆ ในการกำหนดเขตเนื่องจากไม่มีการคั่น การทดสอบบางอย่างบ่งชี้ว่าแม้กระทั่งน้อยถึง 5,000 ไบต์ค่าใช้จ่ายของทางแยกและท่อที่จำเป็นสำหรับการtrมีค่ามันแม้ในกรณีนี้และมันเกือบจะทุกครั้งคือเมื่อตัวเลขสูงขึ้น

ฉันวิ่ง ...

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

...และ...

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

แต่ละชิ้นมีความยาวประมาณ 5 เท่า(ไม่มีข้อมูลเชิงวิทยาศาสตร์ที่นี่ - เพียงอย่างเดียวเท่านั้น)และรุ่นส่วนขยายรั้งเฉลี่ยน้อยกว่า 0.02 วินาทีในเวลาประมวลผลทั้งหมด แต่trรุ่นมาโดยเฉลี่ยประมาณ 0.12 วินาที - และtrรุ่นเอาชนะได้ ทุกเวลา. ฉันไม่สามารถบอกได้ว่าฉันประหลาดใจ - {brace expansion}เป็นคุณสมบัติชวเลขเชลล์แบบโต้ตอบที่มีประโยชน์ แต่มักจะเป็นสิ่งที่ค่อนข้างสิ้นเปลืองในการทำสิ่งที่เกี่ยวข้องกับการเขียนสคริปต์ รูปแบบทั่วไป:

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

... เมื่อคุณคิดเกี่ยวกับมันเป็นสอง forลูป - อย่างแรกคือภายในและส่อให้เห็นว่าเชลล์ต้องวนในบางวิธีเพื่อสร้างตัววนซ้ำก่อนที่จะบันทึกพวกมันทั้งหมดและวนซ้ำอีกครั้งสำหรับforลูปของคุณ สิ่งเหล่านี้มักจะทำได้ดีกว่าเช่น:

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

... เนื่องจากคุณเก็บค่าเพียงไม่กี่ค่าและเขียนทับค่าเหล่านั้นในขณะที่คุณทำเช่นเดียวกับการทำซ้ำในขณะที่คุณสร้างค่า iterables

อย่างไรก็ตามเช่นเดียวกับการเว้นวรรคที่กล่าวถึงก่อนหน้านี้คุณยังสามารถใช้printfzeropad เป็นจำนวนหลักได้ตามต้องการเช่น:

printf %05000d

ฉันทำทั้งสองอย่างโดยไม่มีข้อโต้แย้งเพราะสำหรับทุกอาร์กิวเมนต์ที่ระบุในprintfสตริงรูปแบบของเมื่อข้อโต้แย้งไม่พบสตริงที่เป็นโมฆะถูกใช้ - ซึ่งถูกตีความว่าเป็นศูนย์สำหรับอาร์กิวเมนต์หลักหรือสตริงที่ว่างเปล่าสำหรับสตริง

นี่คือด้านอื่น ๆ(และ - ในความคิดของฉัน -ด้านประสิทธิภาพของเหรียญ) เมื่อเปรียบเทียบกับคำสั่งในคำถาม - ในขณะที่เป็นไปได้ที่จะไม่ได้อะไรจากสิ่งที่คุณทำเมื่อคุณprintf %.0จำกัดความยาวของอาร์กิวเมนต์แต่ละข้อ เป็นไปได้ที่จะได้อะไรจากอะไร

ยังเร็วกว่าสำหรับจำนวนไบต์ที่สร้างขึ้นที่คุณสามารถใช้ddเช่น:

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

... และ w / ไฟล์ปกติdd's seek=[num]อาร์กิวเมนต์สามารถนำมาใช้ให้เป็นประโยชน์มากขึ้น คุณสามารถรับบรรทัดใหม่ 64k แทนที่จะเป็นโมฆะถ้าคุณเพิ่ม,unblock cbs=1ไปด้านบนและจากนั้นสามารถฉีดสตริงตามอำเภอใจต่อบรรทัดด้วยpasteและ/dev/null- แต่ในกรณีนั้นถ้ามันพร้อมให้คุณคุณอาจใช้:

yes 'output string forever'

นี่คือddตัวอย่างเพิ่มเติม:

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

... ซึ่งสร้าง(หรือตัดทอน)\0NULไฟล์เต็มในไดเรกทอรีปัจจุบันชื่อ H.txt ขนาด 5000 ไบต์ ddค้นหาตรงไปยัง offset และ NUL เติมให้เต็ม

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

... ซึ่งสร้างไฟล์ที่มีชื่อและขนาดเดียวกัน แต่เติมตัวอักษร w / H จะใช้ประโยชน์จากddพฤติกรรม spec'd ของการเขียนออกมาอย่างน้อยหนึ่ง null บล็อกเต็มรูปแบบในกรณีของข้อผิดพลาดการอ่านเมื่อnoerrorและsyncแปลงที่ระบุไว้(และ - ไม่count=- มีแนวโน้มที่จะไปนานกว่าที่คุณอาจต้องการ)และจงใจเปลี่ยนเส้นทาง ไฟล์อธิบายอย่างเป็นลายลักษณ์อักษรที่ddstdin ของ


8

%.0sวิธีการแปลงอาร์กิวเมนต์เป็นสตริงที่มีความแม่นยำของศูนย์ ตามman 3 printfค่าความแม่นยำในกรณีเช่นนี้ให้

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

ดังนั้นเมื่อความแม่นยำเป็นศูนย์อาร์กิวเมนต์สตริงจะไม่ถูกพิมพ์เลย อย่างไรก็ตามH(ซึ่งเป็นส่วนหนึ่งของตัวระบุรูปแบบ) ได้รับการพิมพ์หลายครั้งเนื่องจากมีข้อโต้แย้งเนื่องจากเป็นไปตามprintfส่วนของman 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

ในกรณีนี้ให้%.0sพิมพ์ตัวละครหนึ่งตัวที่อยู่ข้างหน้าเสมอ H ในกรณีนี้ เมื่อคุณใช้ {1..5000} เชลล์จะขยายออกและจะกลายเป็น:

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

นั่นคือตอนนี้คำสั่ง printf มีข้อโต้แย้ง 5,000 ข้อและสำหรับแต่ละอาร์กิวเมนต์คุณจะได้รับหนึ่งตัวอักษร H สิ่งเหล่านี้ไม่จำเป็นต้องเรียงตามลำดับหรือเป็นตัวเลข:

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

พิมพ์HHHHH- เช่นจำนวนอาร์กิวเมนต์ 5 ในกรณีนี้

หมายเหตุเครื่องหมายรูปไข่ในตัวอย่างที่ 1 ด้านบนไม่ได้ถูกแทรกอย่างแท้จริงพวกมันอยู่ที่นั่นเพื่อระบุลำดับหรือช่วง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.