ฉันจะเล่นซ้ำอักขระใน Bash ได้อย่างไร


240

ฉันจะทำสิ่งนี้ด้วยได้echoอย่างไร

perl -E 'say "=" x 100'

น่าเศร้านี่ไม่ใช่ Bash
solidsnack

1
ไม่ใช่กับ echo แต่อยู่ในหัวข้อเดียวกันruby -e 'puts "=" * 100'หรือpython -c 'print "=" * 100'
Evgeny

1
เป็นคำถามที่ดีมาก คำตอบที่ดีมาก ฉันใช้หนึ่งในคำตอบในงานจริงที่นี่ฉันจะโพสต์เป็นตัวอย่าง: github.com/drbeco/oldfiles/blob/master/oldfiles (ใช้printfกับseq)svrb=`printf '%.sv' $(seq $vrb)`
Dr Beco

โซลูชันทั่วไปสำหรับพิมพ์สิ่งใดก็ได้ (1 ตัวหรือมากกว่านั้นรวมถึงบรรทัดใหม่): Repeat_this () {i = 1; ในขณะที่ ["$ i" -le "$ 2"]; ทำ printf "% s" "$ 1"; i = $ (($ i + 1)); เสร็จสิ้น; printf '\ n';} ใช้สิ่งนี้: ทำซ้ำสิ่งนี้ "บางอย่าง" Number_of_repetitions ตัวอย่างเช่นในการแสดงซ้ำ 5 ครั้งสิ่งที่รวม 3 บรรทัดใหม่: Repeat_this "$ (printf '\ n \ n \ n \ n นี้')" 5 printf '\ n' สุดท้ายอาจถูกนำออกไป (แต่ฉันใส่ไว้ในการสร้างไฟล์ข้อความและผู้ที่ต้องการขึ้นบรรทัดใหม่เป็นตัวละครตัวสุดท้ายของพวกเขา!)
Olivier Dulac

คำตอบ:


396

คุณสามารถใช้ได้:

printf '=%.0s' {1..100}

มันทำงานอย่างไร:

Bash ขยาย {1..100} ดังนั้นคำสั่งจะกลายเป็น:

printf '=%.0s' 1 2 3 4 ... 100

ฉันได้ตั้งค่ารูปแบบของ printf =%.0sซึ่งหมายความว่ามันจะพิมพ์เพียงครั้งเดียว=ไม่ว่าจะมีการโต้แย้งอะไรก็ตาม ดังนั้นจึงพิมพ์ 100 =วินาที


14
โซลูชันที่ยอดเยี่ยมที่ทำงานได้ดีพอสมควรแม้จะมีจำนวนการทำซ้ำมาก ต่อไปนี้เป็น wrapper ฟังก์ชันที่คุณสามารถเรียกใช้ได้ด้วยrepl = 100( evalจำเป็นต้องใช้repl() { printf "$1"'%.s' $(eval "echo {1.."$(($2))"}"); }
trickery

7
เป็นไปได้หรือไม่ที่จะกำหนดวงเงินสูงสุดโดยใช้ var? ฉันลองแล้วไม่สามารถใช้งานได้
Mike Purcell

70
คุณไม่สามารถใช้ตัวแปรภายในการขยายรั้ง ใช้แทนเช่นseq $(seq 1 $limit)
dogbane

11
หากคุณใช้งานได้สิ่งนี้จะเป็นการดีที่สุดที่จะจัดเรียงใหม่จาก$s%.0sเป็น%.0s$sมิฉะนั้นจะทำให้เกิดprintfข้อผิดพลาด
KomodoDave

5
สิ่งนี้ทำให้ฉันสังเกตเห็นพฤติกรรมของ Bash printf: มันจะยังคงใช้สตริงรูปแบบจนกว่าจะไม่มีข้อโต้แย้งเหลืออยู่ ฉันคิดว่ามันประมวลผลสตริงรูปแบบเพียงครั้งเดียว!
Jeenu

89

ไม่มีวิธีง่ายๆ แต่ตัวอย่างเช่น:

seq -s= 100|tr -d '[:digit:]'

หรืออาจเป็นวิธีที่ได้มาตรฐาน:

printf %100s |tr " " "="

นอกจากนี้ยังมีtput repแต่สำหรับมือถือของฉัน (xterm และ linux) พวกเขาดูเหมือนจะไม่สนับสนุน :)


3
โปรดทราบว่าตัวเลือกแรกที่มี seq จะพิมพ์น้อยกว่าหนึ่งหมายเลขที่กำหนดดังนั้นตัวอย่างจะพิมพ์ 99 =อักขระ
Camilo Martin

13
printf trเป็นเพียงการแก้ปัญหา POSIX เพราะseq, yesและ{1..3}ไม่ได้ POSIX
Ciro Santilli 法轮功冠状病六四事件法轮功

2
ในการทำซ้ำสตริงแทนที่จะเป็นอักขระเดี่ยว: printf %100s | sed 's/ /abc/g'- เอาต์พุต 'abcabcabc ... '
John Rix

3
+1 สำหรับการใช้ไม่มีลูปและมีเพียงหนึ่งคำสั่งภายนอก ( tr) printf "%${COLUMNS}s\n" | tr " " "="นอกจากนี้คุณยังสามารถขยายไปยังสิ่งที่ต้องการ
musiphil

2
@ mklement0 ดีผมก็หวังว่าคุณกำลังนับ newline wcที่ผ่านมาโดยไม่ได้ตั้งใจด้วย ข้อสรุปเดียวที่ฉันทำได้จากนี้คือ " seqไม่ควรใช้"
Camilo Martin

51

เคล็ดลับของหมวกถึง @ gniourf_gniourfสำหรับการป้อนข้อมูลของเขา

หมายเหตุ: คำตอบนี้ไม่ได้ตอบคำถามเดิม แต่เติมเต็มคำตอบที่มีอยู่และเป็นประโยชน์โดยการเปรียบเทียบประสิทธิภาพเปรียบเทียบประสิทธิภาพการทำงาน

โซลูชั่นจะถูกเปรียบเทียบในแง่ของความเร็วในการดำเนินการเท่านั้น - ข้อกำหนดหน่วยความจำไม่ได้เป็นถูกนำมาพิจารณา

สรุป:

  • หากจำนวนการทำซ้ำของคุณมีน้อยพูดได้ถึงประมาณ 100 มันจะคุ้มค่ากับการแก้ปัญหาอย่าง Bash เท่านั้นเนื่องจากต้นทุนเริ่มต้นของระบบสาธารณูปโภคภายนอกมีความสำคัญโดยเฉพาะอย่างยิ่ง Perl
    • ในทางปฏิบัติการพูดอย่างไรก็ตามถ้าคุณต้องการเพียงหนึ่งอินสแตนซ์ของอักขระที่ซ้ำกันโซลูชันที่มีอยู่ทั้งหมดอาจไม่เป็นไร
  • ด้วยขนาดใหญ่นับซ้ำ , ใช้สาธารณูปโภคภายนอกเช่นที่พวกเขาจะได้เร็วขึ้นมาก
    • โดยเฉพาะอย่างยิ่งหลีกเลี่ยงการแทนที่สตริงย่อยทั่วโลกของ Bash ด้วยสตริงขนาดใหญ่
      (เช่น${var// /=}) เนื่องจากมีข้อห้ามที่ช้า

ต่อไปนี้คือเวลาใน iMac ช่วงปลายปี 2012 ด้วยซีพียู Intel Core i5 3.2 GHz และฟิวชั่นไดรฟ์ใช้ OSX 10.10.4 และทุบตี 3.2.57 และเฉลี่ย 1,000 ครั้ง

รายการคือ:

  • แสดงรายการในลำดับของระยะเวลาดำเนินการ (น้อยไปหามาก)
  • นำหน้าด้วย:
    • M... วิธีแก้ปัญหาที่มีหลายตัวเลือก
    • S... ทางออกเดียว -character เท่านั้น
    • P ... โซลูชันที่สอดคล้องกับ POSIX
  • ตามด้วยคำอธิบายสั้น ๆ ของการแก้ปัญหา
  • ต่อท้ายด้วยชื่อของผู้เขียนของคำตอบที่มา

  • จำนวนการนับซ้าเล็กน้อย: 100
[M, P] printf %.s= [dogbane]:                           0.0002
[M   ] printf + bash global substr. replacement [Tim]:  0.0005
[M   ] echo -n - brace expansion loop [eugene y]:       0.0007
[M   ] echo -n - arithmetic loop [Eliah Kagan]:         0.0013
[M   ] seq -f [Sam Salisbury]:                          0.0016
[M   ] jot -b [Stefan Ludwig]:                          0.0016
[M   ] awk - $(count+1)="=" [Steven Penny (variant)]:   0.0019
[M, P] awk - while loop [Steven Penny]:                 0.0019
[S   ] printf + tr [user332325]:                        0.0021
[S   ] head + tr [eugene y]:                            0.0021
[S, P] dd + tr [mklement0]:                             0.0021
[M   ] printf + sed [user332325 (comment)]:             0.0021
[M   ] mawk - $(count+1)="=" [Steven Penny (variant)]:  0.0025
[M, P] mawk - while loop [Steven Penny]:                0.0026
[M   ] gawk - $(count+1)="=" [Steven Penny (variant)]:  0.0028
[M, P] gawk - while loop [Steven Penny]:                0.0028
[M   ] yes + head + tr [Digital Trauma]:                0.0029
[M   ] Perl [sid_com]:                                  0.0059
  • วิธีการแก้ปัญหา Bash เท่านั้นนำแพ็ค - แต่ด้วยซ้ำนับเล็ก ๆ นี้! (ดูด้านล่าง)
  • ต้นทุนเริ่มต้นของยูทิลิตี้ภายนอกมีความสำคัญที่นี่โดยเฉพาะ Perl หากคุณต้องเรียกสิ่งนี้ในลูป - ด้วยการทำซ้ำเล็กๆ ในแต่ละการวนซ้ำ - หลีกเลี่ยงการใช้งานยูทิลิตี้awkและperlการแก้ปัญหา

  • จำนวนซ้ำขนาดใหญ่: 1000000 (1 ล้าน)
[M   ] Perl [sid_com]:                                  0.0067
[M   ] mawk - $(count+1)="=" [Steven Penny (variant)]:  0.0254
[M   ] gawk - $(count+1)="=" [Steven Penny (variant)]:  0.0599
[S   ] head + tr [eugene y]:                            0.1143
[S, P] dd + tr [mklement0]:                             0.1144
[S   ] printf + tr [user332325]:                        0.1164
[M, P] mawk - while loop [Steven Penny]:                0.1434
[M   ] seq -f [Sam Salisbury]:                          0.1452
[M   ] jot -b [Stefan Ludwig]:                          0.1690
[M   ] printf + sed [user332325 (comment)]:             0.1735
[M   ] yes + head + tr [Digital Trauma]:                0.1883
[M, P] gawk - while loop [Steven Penny]:                0.2493
[M   ] awk - $(count+1)="=" [Steven Penny (variant)]:   0.2614
[M, P] awk - while loop [Steven Penny]:                 0.3211
[M, P] printf %.s= [dogbane]:                           2.4565
[M   ] echo -n - brace expansion loop [eugene y]:       7.5877
[M   ] echo -n - arithmetic loop [Eliah Kagan]:         13.5426
[M   ] printf + bash global substr. replacement [Tim]:  n/a
  • โซลูชัน Perl จากคำถามนั้นเร็วที่สุด
  • การแทนที่สตริงทั่วโลกของ Bash ( ${foo// /=}) นั้นช้ามากอย่างน่าอัศจรรย์กับสตริงจำนวนมากและถูกนำออกจากการแข่งขัน (ใช้เวลาประมาณ 50 นาที (!) ใน Bash 4.3.30 และนานกว่าใน Bash 3.2.57 - ฉันไม่เคยรอ เสร็จสิ้น)
  • Bash loops นั้นช้าและ arithmetic loops ( (( i= 0; ... ))) จะช้ากว่า brace-extended ( {1..n}) - ถึงแม้ว่า arithmetic loops จะมีประสิทธิภาพในการใช้หน่วยความจำมากกว่า
  • awkอ้างถึงBSD awk (เท่าที่พบใน OSX) - มันช้ากว่าอย่างเห็นได้ชัดgawk(GNU Awk) และโดยเฉพาะอย่างยิ่งmawkและโดยเฉพาะอย่างยิ่ง
  • โปรดทราบว่าด้วยจำนวนมากและหลายตัวอักษร สตริงการใช้หน่วยความจำอาจกลายเป็นการพิจารณา - แนวทางที่แตกต่างกันในเรื่องนั้น

นี่คือสคริปต์ Bash ( testrepeat) ที่สร้างรายการด้านบน ใช้เวลา 2 ข้อโต้แย้ง:

  • จำนวนตัวอักษรซ้ำ
  • เป็นทางเลือกจำนวนของการทดสอบที่จะดำเนินการและคำนวณเวลาเฉลี่ยจาก

กล่าวอีกนัยหนึ่ง: การกำหนดเวลาด้านบนได้ด้วยtestrepeat 100 1000และtestrepeat 1000000 1000

#!/usr/bin/env bash

title() { printf '%s:\t' "$1"; }

TIMEFORMAT=$'%6Rs'

# The number of repetitions of the input chars. to produce
COUNT_REPETITIONS=${1?Arguments: <charRepeatCount> [<testRunCount>]}

# The number of test runs to perform to derive the average timing from.
COUNT_RUNS=${2:-1}

# Discard the (stdout) output generated by default.
# If you want to check the results, replace '/dev/null' on the following
# line with a prefix path to which a running index starting with 1 will
# be appended for each test run; e.g., outFilePrefix='outfile', which
# will produce outfile1, outfile2, ...
outFilePrefix=/dev/null

{

  outFile=$outFilePrefix
  ndx=0

  title '[M, P] printf %.s= [dogbane]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  # !! In order to use brace expansion with a variable, we must use `eval`.
  eval "
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    printf '%.s=' {1..$COUNT_REPETITIONS} >"$outFile"
  done"

  title '[M   ] echo -n - arithmetic loop [Eliah Kagan]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    for ((i=0; i<COUNT_REPETITIONS; ++i)); do echo -n =; done >"$outFile"
  done


  title '[M   ] echo -n - brace expansion loop [eugene y]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  # !! In order to use brace expansion with a variable, we must use `eval`.
  eval "
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    for i in {1..$COUNT_REPETITIONS}; do echo -n =; done >"$outFile"
  done
  "

  title '[M   ] printf + sed [user332325 (comment)]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    printf "%${COUNT_REPETITIONS}s" | sed 's/ /=/g' >"$outFile"
  done


  title '[S   ] printf + tr [user332325]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    printf "%${COUNT_REPETITIONS}s" | tr ' ' '='  >"$outFile"
  done


  title '[S   ] head + tr [eugene y]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    head -c $COUNT_REPETITIONS < /dev/zero | tr '\0' '=' >"$outFile"
  done


  title '[M   ] seq -f [Sam Salisbury]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    seq -f '=' -s '' $COUNT_REPETITIONS >"$outFile"
  done


  title '[M   ] jot -b [Stefan Ludwig]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    jot -s '' -b '=' $COUNT_REPETITIONS >"$outFile"
  done


  title '[M   ] yes + head + tr [Digital Trauma]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    yes = | head -$COUNT_REPETITIONS | tr -d '\n'  >"$outFile"
  done

  title '[M   ] Perl [sid_com]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    perl -e "print \"=\" x $COUNT_REPETITIONS" >"$outFile"
  done

  title '[S, P] dd + tr [mklement0]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    dd if=/dev/zero bs=$COUNT_REPETITIONS count=1 2>/dev/null | tr '\0' "=" >"$outFile"
  done

  # !! On OSX, awk is BSD awk, and mawk and gawk were installed later.
  # !! On Linux systems, awk may refer to either mawk or gawk.
  for awkBin in awk mawk gawk; do
    if [[ -x $(command -v $awkBin) ]]; then

      title "[M   ] $awkBin"' - $(count+1)="=" [Steven Penny (variant)]'
      [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
      time for (( n = 0; n < COUNT_RUNS; n++ )); do 
        $awkBin -v count=$COUNT_REPETITIONS 'BEGIN { OFS="="; $(count+1)=""; print }' >"$outFile"
      done

      title "[M, P] $awkBin"' - while loop [Steven Penny]'
      [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
      time for (( n = 0; n < COUNT_RUNS; n++ )); do 
        $awkBin -v count=$COUNT_REPETITIONS 'BEGIN { while (i++ < count) printf "=" }' >"$outFile"
      done

    fi
  done

  title '[M   ] printf + bash global substr. replacement [Tim]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  # !! In Bash 4.3.30 a single run with repeat count of 1 million took almost
  # !! 50 *minutes*(!) to complete; n Bash 3.2.57 it's seemingly even slower -
  # !! didn't wait for it to finish.
  # !! Thus, this test is skipped for counts that are likely to be much slower
  # !! than the other tests.
  skip=0
  [[ $BASH_VERSINFO -le 3 && COUNT_REPETITIONS -gt 1000 ]] && skip=1
  [[ $BASH_VERSINFO -eq 4 && COUNT_REPETITIONS -gt 10000 ]] && skip=1
  if (( skip )); then
    echo 'n/a' >&2
  else
    time for (( n = 0; n < COUNT_RUNS; n++ )); do 
      { printf -v t "%${COUNT_REPETITIONS}s" '='; printf %s "${t// /=}"; } >"$outFile"
    done
  fi
} 2>&1 | 
 sort -t$'\t' -k2,2n | 
   awk -F $'\t' -v count=$COUNT_RUNS '{ 
    printf "%s\t", $1; 
    if ($2 ~ "^n/a") { print $2 } else { printf "%.4f\n", $2 / count }}' |
     column -s$'\t' -t

เป็นที่น่าสนใจที่จะเห็นการเปรียบเทียบเวลา แต่ฉันคิดว่าในหลาย ๆ โปรแกรมมีบัฟเฟอร์ผลลัพธ์ดังนั้นเวลาของพวกเขาสามารถเปลี่ยนแปลงได้หากบัฟเฟอร์ถูกปิด
Sergiy Kolodyazhnyy

In order to use brace expansion with a variable, we must use `eval`👍
pyb

2
ดังนั้นโซลูชัน perl (sid_com) จึงเป็นวิธีที่เร็วที่สุด ... เมื่อถึงค่าใช้จ่ายเริ่มต้นของการเปิดใช้ perl แล้ว (มันเปลี่ยนจาก 59ms สำหรับการทำซ้ำเพียงเล็กน้อยถึง 67ms สำหรับการทำซ้ำนับล้าน ... ดังนั้นการ perl forking ใช้เวลาประมาณ 59ms ในระบบของคุณ)
Olivier Dulac

46

มีมากกว่าหนึ่งวิธีที่จะทำ

ใช้วง:

  • การขยายรั้งสามารถใช้กับตัวอักษรจำนวนเต็ม:

    for i in {1..100}; do echo -n =; done    
  • การวนรอบเหมือน C ช่วยให้สามารถใช้ตัวแปรได้:

    start=1
    end=100
    for ((i=$start; i<=$end; i++)); do echo -n =; done

ใช้printfbuiltin:

printf '=%.0s' {1..100}

การระบุความแม่นยำที่นี่ตัดสตริงให้พอดีกับความกว้างที่ระบุ ( 0) เมื่อprintfนำสตริงการจัดรูปแบบกลับมาใช้เพื่อใช้อาร์กิวเมนต์ทั้งหมดพิมพ์เพียง"="100 ครั้ง

ใช้head( printf, ฯลฯ ) และtr:

head -c 100 < /dev/zero | tr '\0' '='
printf %100s | tr " " "="

2
++ สำหรับhead/ trโซลูชันซึ่งทำงานได้ดีแม้จะมีจำนวนการทำซ้ำสูง (คำเตือนเล็ก ๆ : head -cไม่สอดคล้องกับ POSIX แต่ทั้ง BSD และ GNU headใช้งานได้); ในขณะที่อีกสองโซลูชั่นจะช้าในกรณีนั้นพวกเขามีข้อได้เปรียบในการทำงานกับสตริงหลายตัวเช่นกัน
mklement0

1
การใช้yesและhead- yes "" | head -n 100มีประโยชน์ถ้าคุณต้องการจำนวนหนึ่งของการขึ้นบรรทัดใหม่: trสามารถทำให้มันพิมพ์ตัวอักษรใด ๆ :yes "" | head -n 100 | tr "\n" "="; echo
loxaxs

ค่อนข้างน่าแปลกใจ: dd if=/dev/zero count=1 bs=100000000 | tr '\0' '=' >/dev/nullช้ากว่าhead -c100000000 < /dev/zero | tr '\0' '=' >/dev/nullรุ่นมาก แน่นอนคุณต้องใช้ขนาดบล็อก 100M + เพื่อวัดความแตกต่างของเวลาอย่างสมเหตุสมผล 100M bytes ใช้เวลา 1.7 วินาทีและ 1 วินาทีพร้อมทั้งสองรุ่นตามลำดับที่แสดง ฉันถอด tr และเพิ่งทิ้งไป/dev/nullและรับ 0.287 s สำหรับheadรุ่นและ 0.675 s สำหรับddรุ่นเป็นพันล้านไบต์
Michael Goldshteyn

สำหรับ: dd if=/dev/zero count=1 bs=100000000 | tr '\0' '=' >/dev/null=> 0,21332 s, 469 MB/s; สำหรับ: dd if=/dev/zero count=100 bs=1000000| tr '\0' '=' >/dev/null=> 0,161579 s, 619 MB/s;
3ED

31

ฉันเพิ่งพบวิธีที่ง่ายอย่างจริงจังในการทำเช่นนี้โดยใช้ seq:

อัปเดต: ใช้งานได้กับ BSD seqที่มาพร้อมกับ OS X. YMMV กับเวอร์ชันอื่น

seq  -f "#" -s '' 10

จะพิมพ์ '#' 10 ครั้งเช่นนี้

##########
  • -f "#"กำหนดสตริงรูปแบบเพื่อละเว้นตัวเลขและเพียงพิมพ์#สำหรับแต่ละคน
  • -s '' ตั้งค่าตัวคั่นเป็นสตริงว่างเพื่อลบบรรทัดใหม่ที่ seq แทรกระหว่างแต่ละหมายเลข
  • ช่องว่างหลัง-fและ-sดูเหมือนจะมีความสำคัญ

แก้ไข: ที่นี่มันอยู่ในฟังก์ชั่นที่มีประโยชน์ ...

repeat () {
    seq  -f $1 -s '' $2; echo
}

ซึ่งคุณสามารถโทรแบบนี้ ...

repeat "#" 10

หมายเหตุ:หากคุณกำลังทำซ้ำ#คำพูดนั้นมีความสำคัญ!


7
seq: format ‘#’ has no % directiveนี้จะช่วยให้ฉัน seqใช้สำหรับตัวเลขไม่ใช่สตริง ดูgnu.org/software/coreutils/manual/html_node/seq-invocation.html
John B

อาดังนั้นฉันใช้ seq รุ่น BSD ที่พบใน OS X ฉันจะอัปเดตคำตอบ คุณใช้เวอร์ชั่นไหน
Sam Salisbury

ฉันใช้ seq จาก coreutils ของ GNU
John B

1
@JohnB: BSD seqกำลังrepurposedที่นี่อย่างชาญฉลาดเพื่อทำซ้ำสตริง : สตริงรูปแบบที่ส่งผ่าน-f- ปกติใช้เพื่อจัดรูปแบบตัวเลขที่สร้างขึ้น - มีเฉพาะสตริงที่จะทำซ้ำที่นี่เพื่อให้ผลลัพธ์มีสำเนาของสตริงนั้นเท่านั้น น่าเสียดายที่ GNU seqยืนยันว่ามีรูปแบบตัวเลขในสตริงรูปแบบซึ่งเป็นข้อผิดพลาดที่คุณเห็น
mklement0

1
ทำได้ดีมาก ยังทำงานร่วมกับสตริงตัวอักษรหลายตัว โปรดใช้"$1"(เครื่องหมายคำพูดคู่) เพื่อให้คุณสามารถส่งผ่านตัวอักษรเช่น'*'และสตริงที่มีช่องว่างในตัว สุดท้ายหากคุณต้องการใช้งาน%คุณต้องเพิ่มเป็นสองเท่า (มิฉะนั้นseqจะคิดว่าเป็นส่วนหนึ่งของข้อกำหนดรูปแบบเช่น%f); การใช้"${1//%/%%}"จะดูแลที่ ตั้งแต่ (อย่างที่คุณพูดถึง) คุณกำลังใช้ BSD seqนี้จะทำงานบน BSD เหมือนระบบปฏิบัติการทั่วไป (เช่น FreeBSD) - โดยคมชัดมันจะไม่ทำงานบนลินุกซ์ที่GNU seqถูกนำมาใช้
mklement0

18

นี่คือสองวิธีที่น่าสนใจ:

ubuntu @ ubuntu: ~ $ yes = | หัว -10 | วาง -s -d '' -
==========
ubuntu @ ubuntu: ~ $ yes = | หัว -10 | tr -d "\ n"
========== อูบุนตู @ อูบุนตู: ~ $ 

หมายเหตุทั้งสองนี้มีความแตกต่างกันเล็กน้อย - pasteวิธีสิ้นสุดในบรรทัดใหม่ trวิธีการไม่ได้


1
ทำได้ดีมาก โปรดทราบว่าBSD pasteอธิบายอย่างไม่ถูกต้อง-d '\0'สำหรับการระบุตัวคั่นที่ว่างเปล่าและล้มเหลวด้วย-d ''- -d '\0'ควรทำงานด้วยการใช้งาน POSIX ที่เข้ากันได้ทั้งหมดpasteและทำงานได้กับGNU pasteเช่นกัน
mklement0

ในทำนองเดียวกันด้วยเครื่องมือติดตั้งนอกเรือที่น้อยลง:yes | mapfile -n 100 -C 'printf = \#' -c 1
อธิการ

@ อธิการ: แม้ว่าคำสั่งของคุณจะสร้าง subshell น้อยลงหนึ่งอัน แต่ก็ยังช้ากว่าสำหรับจำนวนการทำซ้ำที่สูงขึ้นและสำหรับการทำซ้ำที่ต่ำกว่าการนับความแตกต่างอาจไม่สำคัญ ขีด จำกัด ที่แน่นอนน่าจะเป็นทั้งฮาร์ดแวร์และระบบปฏิบัติการเช่นบนเครื่อง OSX 10.11.5 ของฉันคำตอบนี้เร็วกว่าที่ 500 แล้ว time yes = | head -500 | paste -s -d '\0' -; time yes | mapfile -n 500 -C 'printf = \#' -c 1ความพยายาม อย่างไรก็ตามที่สำคัญกว่านั้น: หากคุณกำลังใช้printfอยู่คุณอาจใช้วิธีที่ง่ายและมีประสิทธิภาพมากขึ้นจากคำตอบที่ยอมรับ:printf '%.s=' $(seq 500)
mklement0

13

ไม่มีวิธีง่ายๆ หลีกเลี่ยงการใช้ลูปprintfและการแทนที่

str=$(printf "%40s")
echo ${str// /rep}
# echoes "rep" 40 times.

2
ดี แต่ทำงานได้ดีพอสมควรด้วยจำนวนการทำซ้ำเพียงเล็กน้อย นี่คือ wrapper ของฟังก์ชันที่สามารถเรียกใช้เป็นrepl = 100ตัวอย่างเช่น (ไม่แสดงผลลัพธ์ต่อท้าย\n):repl() { local ts=$(printf "%${2}s"); printf %s "${ts// /$1}"; }
mklement0

1
@ mklement0 ยินดีที่ได้มอบรุ่นฟังก์ชั่นของทั้งสองโซลูชั่น +1 ให้ทั้งคู่!
Camilo Martin

8

หากคุณต้องการความสอดคล้อง POSIX และความสอดคล้องในการใช้งานที่แตกต่างกันของechoและprintfและ / หรือเชลล์อื่นที่ไม่ใช่แค่bash:

seq(){ n=$1; while [ $n -le $2 ]; do echo $n; n=$((n+1)); done ;} # If you don't have it.

echo $(for each in $(seq 1 100); do printf "="; done)

... จะสร้างผลลัพธ์เดียวกันperl -E 'say "=" x 100'กับทุกที่


1
ปัญหาคือseqไม่ได้เป็นยูทิลิตี้ POSIX (แม้ว่าระบบ BSD และ Linux จะมีการใช้งาน) - คุณสามารถทำเลขคณิตเชลล์ POSIX ด้วยwhileลูปแทนได้ดังที่คำตอบของ @ Xennex81 ( printf "="ตามที่คุณแนะนำถูกต้องมากกว่าecho -n)
mklement0

1
โอ๊ะคุณพูดถูกแล้ว บางสิ่งบางอย่างเช่นที่ผ่านมาฉันบางครั้งเป็นมาตรฐานที่ทำให้รู้สึกไม่สบาย calPOSIX คือ seqไม่ใช่. อย่างไรก็ตามแทนที่จะเขียนคำตอบใหม่อีกครั้งด้วยการวนรอบสักครู่ (อย่างที่คุณพูดนั่นคือคำตอบอื่น ๆ อยู่แล้ว) ฉันจะเพิ่มฟังก์ชั่น RYO วิธีการศึกษาเพิ่มเติม ;-)
Geoff Nixon

8

คำถามคือทำอย่างไรกับecho:

echo -e ''$_{1..100}'\b='

สิ่งนี้จะทำเช่นเดียวกันperl -E 'say "=" x 100'กับที่มี แต่echoเท่านั้น


ตอนนี้เป็นเรื่องผิดปกติถ้าคุณไม่ได้รวมช่องว่างด้านหลังไว้ในนั้นหรือทำความสะอาดโดยใช้: echo -e $ _ {1..100} '\ b =' | col
anthony

1
ความคิดที่ไม่ดี สิ่งนี้จะล้มเหลวหาก$_1, $_2หรือตัวแปรอื่น ๆ นับร้อยมีค่า
John Kugelman

@JohnKugelman echo $ (set -; eval echo -e \ $ {{1..100}} '\\ b =')
mug896

นี่คือขั้นต้น ฉันชอบมัน: D
dimo414

6

วิธีทุบตีบริสุทธิ์โดยไม่ eval , ไม่มี subshells, ไม่มีเครื่องมือภายนอก, ไม่มีการขยายรั้ง (เช่นคุณสามารถมีหมายเลขที่จะทำซ้ำในตัวแปร):

หากคุณได้รับตัวแปรnที่ขยายไปยังหมายเลข (ไม่ใช่ลบ) และตัวแปรpatternเช่น

$ n=5
$ pattern=hello
$ printf -v output '%*s' "$n"
$ output=${output// /$pattern}
$ echo "$output"
hellohellohellohellohello

คุณสามารถสร้างฟังก์ชั่นด้วยสิ่งนี้:

repeat() {
    # $1=number of patterns to repeat
    # $2=pattern
    # $3=output variable name
    local tmp
    printf -v tmp '%*s' "$1"
    printf -v "$3" '%s' "${tmp// /$2}"
}

ด้วยชุดนี้:

$ repeat 5 hello output
$ echo "$output"
hellohellohellohellohello

สำหรับเคล็ดลับเล็ก ๆ นี้เราใช้printfมันค่อนข้างมากกับ:

  • -v varname: แทนการพิมพ์ไปออกมาตรฐานจะใส่เนื้อหาของสตริงรูปแบบในตัวแปรprintfvarname
  • '% * s': printfจะใช้อาร์กิวเมนต์เพื่อพิมพ์จำนวนช่องว่างที่สอดคล้องกัน เช่นprintf '%*s' 42จะพิมพ์ 42 ช่องว่าง
  • สุดท้ายเมื่อเรามีจำนวนที่ต้องการของช่องว่างในตัวแปรของเราเราใช้การขยายตัวพารามิเตอร์เพื่อแทนที่ช่องว่างทั้งหมดตามรูปแบบของเรา: ${var// /$pattern}จะขยายไปยังการขยายตัวของที่มีช่องว่างทั้งหมดแทนที่ด้วยการขยายตัวของvar$pattern

คุณสามารถกำจัดtmpตัวแปรในrepeatฟังก์ชันโดยใช้การขยายทางอ้อม:

repeat() {
    # $1=number of patterns to repeat
    # $2=pattern
    # $3=output variable name
    printf -v "$3" '%*s' "$1"
    printf -v "$3" '%s' "${!3// /$2}"
}

รูปแบบที่น่าสนใจเพื่อส่งผ่านชื่อตัวแปรในขณะที่วิธีนี้ใช้ได้ดีสำหรับการทำซ้ำนับได้ถึง 1,000 (และอาจจะดีสำหรับแอปพลิเคชันในชีวิตจริงส่วนใหญ่ถ้าฉันเดา) มันจะช้ามากสำหรับการนับที่สูงขึ้น แสดงความคิดเห็น)
mklement0

ดูเหมือนว่าbashการดำเนินการเปลี่ยนสตริงทั่วโลกในบริบทของการขยายพารามิเตอร์ ( ${var//old/new}) ช้าโดยเฉพาะอย่างยิ่ง: ช้าทุบตีเลือดตาแทบกระเด็น3.2.57และช้าทุบตี4.3.30อย่างน้อยในระบบ OSX 10.10.3 ของฉันบนเครื่อง 3.2 Intel Core i5 3.2 Ghz: ด้วย จำนวน 1,000 สิ่งต่าง ๆ ช้า ( 3.2.57) / เร็ว ( 4.3.30): 0.1 / 0.004 วินาที การเพิ่มจำนวนถึง 10,000 ให้ผลต่างตัวเลขที่โดดเด่น: repeat 10000 = varใช้เวลาประมาณ 80 วินาที (!) ในการทุบตี3.2.57และประมาณ 0.3 วินาทีในการทุบตี4.3.30(เร็วกว่า3.2.57มาก แต่ก็ยังช้า)
mklement0

6
#!/usr/bin/awk -f
BEGIN {
  OFS = "="
  NF = 100
  print
}

หรือ

#!/usr/bin/awk -f
BEGIN {
  while (z++ < 100) printf "="
}

ตัวอย่าง


3
ทำได้ดีมาก สิ่งนี้สอดคล้องกับ POSIX และเร็วพอสมควรแม้มีจำนวนการทำซ้ำสูงในขณะที่รองรับสตริงอักขระหลายตัว awk 'BEGIN { while (c++ < 100) printf "=" }'นี่คือรุ่นเปลือก: ห่อเป็นฟังก์ชั่นเปลือกแปร (วิงวอนขอเป็นตัวอย่าง):repeat 100 = repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { txt=substr(txt, 2); while (i++ < count) printf txt }'; }( จำเป็นต้องใช้.คำนำหน้าdummy char และการsubstrเรียกใช้เพิ่มเติมเพื่อแก้ไขข้อบกพร่องใน BSD awkซึ่งผ่านค่าตัวแปรที่เริ่มต้นด้วยการ=แบ่งคำสั่ง)
mklement0

1
NF = 100แก้ปัญหาคือฉลาดมาก (แม้ว่าจะได้รับ 100 =คุณต้องใช้NF = 101) คำเตือนที่ว่ามันเกิดปัญหา BSD awk( แต่มันเป็นไปอย่างรวดเร็วมากด้วยgawkและเร็วยิ่งขึ้นด้วยmawk) และที่กล่าวถึงใน POSIX ค่ากำหนดที่จะNFไม่ใช้เขตข้อมูลในBEGINบล็อก คุณสามารถทำให้มันทำงานใน BSD awkด้วยการปรับแต่งเล็กน้อย: awk 'BEGIN { OFS = "="; $101=""; print }'(แต่อยากรู้อยากเห็นใน BSD awkที่ไม่เร็วกว่าสารละลายลูป) repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { OFS=substr(txt, 2); $(count+1)=""; print }'; }ในฐานะที่เป็นวิธีการแก้ปัญหาเปลือกแปร:
mklement0

หมายเหตุสำหรับผู้ใช้ - เคล็ดลับ NF = 100 ทำให้เกิดข้อผิดพลาดในส่วนของ awk รุ่นเก่า นี่original-awkคือชื่อภายใต้ Linux ของ awk รุ่นเก่าซึ่งคล้ายกับ awk ของ BSD ซึ่งได้รับการรายงานว่าล้มเหลวเช่นกันหากคุณต้องการลอง โปรดทราบว่าการหยุดทำงานมักเป็นขั้นตอนแรกในการค้นหาข้อบกพร่องที่สามารถใช้ประโยชน์ได้ คำตอบนี้ส่งเสริมรหัสที่ไม่ปลอดภัย

2
หมายเหตุสำหรับผู้ใช้ - original-awkไม่ได้มาตรฐานและไม่แนะนำ
Steven Penny

ทางเลือกอื่นสำหรับโค้ดขนาดสั้นอาจเป็นawk NF=100 OFS='=' <<< ""(ใช้bashและgawk)
oliv

4

ฉันเดาว่าจุดประสงค์ดั้งเดิมของคำถามก็คือทำสิ่งนี้ด้วยคำสั่งในตัวของเชลล์ ดังนั้นforloops และprintfs จะถูกต้องตามกฎหมายในขณะที่rep, perlและjotด้านล่างจะไม่ ยังคงคำสั่งดังต่อไปนี้

jot -s "/" -b "\\" $((COLUMNS/2))

ตัวอย่างเช่นพิมพ์บรรทัดกว้างของหน้าต่าง \/\/\/\/\/\/\/\/\/\/\/\/


2
ทำได้ดีมาก ใช้งานได้ดีแม้จะมีการนับซ้ำสูง (ในขณะที่รองรับสตริงอักขระหลายตัว) เพื่อแสดงให้เห็นถึงวิธีการที่ดีกว่าที่นี่เทียบเท่ากับคำสั่งของ OP jot -s '' -b '=' 100ที่: ข้อแม้คือในขณะที่ BSD-เช่นแพลตฟอร์มรวมทั้ง OSX มาพร้อมกับjot, distros ลินุกซ์ไม่ได้
mklement0

1
ขอบคุณฉันชอบการใช้ -s '' ที่ดียิ่งขึ้น ฉันเปลี่ยนสคริปต์แล้ว
สเตฟานลุดวิก

เมื่อวันที่ผ่านมาDebian -based ระบบจะให้apt install athena-jot jot
agc

4

ดังที่คนอื่น ๆ กล่าวไว้ในการขยาย bash braceนำหน้าการขยายพารามิเตอร์ดังนั้นช่วงสามารถมีได้เพียงตัวอักษรเท่านั้น และมอบโซลูชันที่สะอาด แต่ไม่สามารถพกพาได้อย่างสมบูรณ์จากระบบหนึ่งไปยังอีกระบบแม้ว่าคุณจะใช้เชลล์ตัวเดียวกันในแต่ละระบบ (แม้ว่าจะมีให้บริการเพิ่มมากขึ้นเช่นใน FreeBSD 9.3 และสูงกว่า ) และการอ้อมในรูปแบบอื่น ๆ มักจะใช้งานได้ แต่ไม่ค่อยมีความเหมาะสม{m,n}seqjotseqeval

โชคดีที่ทุบตีรองรับ C-style สำหรับลูป (ที่มีนิพจน์ทางคณิตศาสตร์เท่านั้น) ดังนั้นนี่คือวิธี "ทุบตีบริสุทธิ์" โดยย่อ:

repecho() { for ((i=0; i<$1; ++i)); do echo -n "$2"; done; echo; }

สิ่งนี้ใช้จำนวนการทำซ้ำเป็นอาร์กิวเมนต์แรกและสตริงที่จะทำซ้ำ (ซึ่งอาจเป็นอักขระเดียวเช่นเดียวกับในคำอธิบายปัญหา) เป็นอาร์กิวเมนต์ที่สอง repecho 7 bเอาต์พุตbbbbbbb(ถูกยกเลิกโดยขึ้นบรรทัดใหม่)

เดนนิสวิลเลียมสันให้เป็นหลักแก้ปัญหานี้สี่ปีที่ผ่านมาในคำตอบที่ดีเยี่ยมของเขาในการสร้างสายอักขระซ้ำในเชลล์สคริปต์ ฟังก์ชั่นร่างกายของฉันแตกต่างจากรหัสที่นั่นเล็กน้อย:

  • เนื่องจากมุ่งเน้นที่นี่อยู่ในการทำซ้ำตัวเดียวและเปลือกเป็นทุบตีมันอาจจะปลอดภัยที่จะใช้แทนecho และผมอ่านคำอธิบายปัญหาที่เกิดขึ้นในคำถามนี้เช่นการแสดงการตั้งค่าการพิมพ์ด้วยprintf echoความหมายของฟังก์ชั่นข้างต้นทำงานในทุบตีและksh93 แม้ว่าprintfจะพกพาได้มากกว่า (และโดยปกติควรใช้กับสิ่งของประเภทนี้) แต่echoไวยากรณ์ของอ่านง่ายกว่า

    บางหอยechobuiltins ตีความ-ด้วยตัวเองเป็นตัวเลือก - แม้ว่าความหมายตามปกติของ-การใช้ stdin echoสำหรับการป้อนข้อมูลเป็นไร้สาระสำหรับ zshทำสิ่งนี้ และมีแน่นอนอยู่echoของที่ไม่รู้จัก-nเป็นมันไม่ได้มาตรฐาน (กระสุนสไตล์ Bourne จำนวนมากไม่ยอมรับ C-style สำหรับลูปเลยดังนั้นechoพฤติกรรมของพวกเขาไม่จำเป็นต้องพิจารณา .. )

  • ที่นี่งานคือการพิมพ์ลำดับ; ตรงนั้นมันเป็นการกำหนดให้ตัวแปร

หาก$nเป็นจำนวนซ้ำที่ต้องการและคุณไม่ต้องใช้ซ้ำและคุณต้องการบางสิ่งที่สั้นกว่า:

while ((n--)); do echo -n "$s"; done; echo

nต้องเป็นตัวแปร - วิธีนี้ใช้ไม่ได้กับพารามิเตอร์ตำแหน่ง $sเป็นข้อความที่จะต้องทำซ้ำ


2
หลีกเลี่ยงการทำเวอร์ชันลูปอย่างมาก printf "%100s" | tr ' ' '='เหมาะสมที่สุด
ocodo

ข้อมูลพื้นฐานที่ดีและความรุ่งโรจน์สำหรับบรรจุภัณฑ์การทำงานเป็นฟังก์ชั่นซึ่งทำงานได้zshดีเช่นกัน วิธี echo-in-a-loop ทำงานได้ดีสำหรับจำนวนการทำซ้ำที่น้อยกว่า แต่สำหรับตัวที่ใหญ่กว่านั้นมีตัวเลือกที่สอดคล้องกับ POSIX บนพื้นฐานของยูทิลิตี้ตามความเห็นของ @ Slomojo
mklement0

การเพิ่มวงเล็บรอบลูปที่สั้นกว่าของคุณจะรักษาคุณค่าของ n โดยไม่มีผลต่อ echos:(while ((n--)); do echo -n "$s"; done; echo)

ใช้ printf แทน echo! มันเป็นวิธีที่พกพาได้มากขึ้น (echo -n สามารถทำงานได้ในบางระบบเท่านั้น) ดูunix.stackexchange.com/questions/65803/… (หนึ่งในคำตอบที่น่ากลัวของ Stephane Chazelas)
Olivier Dulac

@OlivierDulac คำถามที่นี่เป็นเรื่องเกี่ยวกับทุบตี ไม่ว่าระบบที่คุณกำลังทำงานในการดำเนินงานได้หากคุณใช้ทุบตีมันทุบตีมีในตัวที่รองรับecho -nวิญญาณของสิ่งที่คุณพูดนั้นถูกต้องอย่างแน่นอน printfควรเป็นที่ต้องการechoอย่างน้อยที่สุดในการใช้งานแบบไม่โต้ตอบ แต่ผมไม่คิดว่ามันเป็นไปในทางใด ๆ ที่ไม่เหมาะสมหรือทำให้เข้าใจผิดที่จะให้echoคำตอบสำหรับคำถามที่ถามหาหนึ่งและที่ให้ข้อมูลเพียงพอที่จะรู้ว่ามันจะทำงาน โปรดทราบด้วยว่าการสนับสนุน((n--))(โดยไม่มี$) จะไม่รับประกันโดย POSIX
Eliah Kagan

4

Python แพร่หลายและทำงานได้ทุกที่

python -c "import sys; print('*' * int(sys.argv[1]))" "=" 100

ตัวละครและการนับจะถูกส่งเป็นพารามิเตอร์แยกต่างหาก


ฉันคิดว่านี่เป็นเจตนาที่นี่python -c "import sys; print(sys.argv[1] * int(sys.argv[2]))" "=" 100
gazhay

@loevborg มันไม่ค่อยไกลขนาดนั้นเลยเหรอ?
Sapphire_Brick


3

อีกวิธีหนึ่งในการทำซ้ำสตริงโดยพลการ n ครั้ง:

ข้อดี:

  • ทำงานร่วมกับ POSIX เชลล์
  • สามารถกำหนดเอาต์พุตให้กับตัวแปรได้
  • ทำซ้ำสตริงใด ๆ
  • เร็วมากถึงแม้จะมีการทำซ้ำขนาดใหญ่มาก

จุดด้อย:

  • ต้องการyesคำสั่งของ Gnu Core Utils
#!/usr/bin/sh
to_repeat='='
repeat_count=80
yes "$to_repeat" | tr -d '\n' | head -c "$repeat_count"

ด้วยเทอร์มินัล ANSI และอักขระ US-ASCII เพื่อทำซ้ำ คุณสามารถใช้ลำดับ escape ของ ANSI CSI มันเป็นวิธีที่เร็วที่สุดในการทำซ้ำตัวละคร

#!/usr/bin/env bash

char='='
repeat_count=80
printf '%c\e[%db' "$char" "$repeat_count"

หรือแบบคงที่:

พิมพ์บรรทัด 80 ครั้ง=:

printf '=\e[80b\n'

ข้อ จำกัด :

  • ไม่ใช่เทอร์มินัลทั้งหมดที่เข้าใจrepeat_charลำดับ ANSI CSI
  • สามารถทำซ้ำอักขระ ISO-ASCII หรือไบต์เดียวเท่านั้น
  • หยุดซ้ำที่คอลัมน์สุดท้ายดังนั้นคุณสามารถใช้ค่าขนาดใหญ่เพื่อเติมทั้งบรรทัดได้โดยไม่คำนึงถึงความกว้างของเทอร์มินัล
  • การทำซ้ำมีไว้สำหรับแสดงผลเท่านั้น การบันทึกเอาต์พุตลงในตัวแปรเชลล์จะไม่ขยายrepeat_charลำดับ ANSI CSI เป็นอักขระซ้ำ

1
หมายเหตุเล็กน้อย - REP (CSI b) ควรพันโดยปกติหากเครื่องอยู่ในโหมดตัด
jerch

3

นี่คือสิ่งที่ฉันใช้พิมพ์บรรทัดของอักขระผ่านหน้าจอใน linux (ตามความกว้างของหน้าจอเทอร์มินัล / หน้าจอ)

พิมพ์ "=" ผ่านหน้าจอ:

printf '=%.0s' $(seq 1 $(tput cols))

คำอธิบาย:

พิมพ์เครื่องหมายเท่ากับหลาย ๆ ครั้งตามลำดับที่กำหนด:

printf '=%.0s' #sequence

ใช้เอาต์พุตของคำสั่ง (นี่คือคุณลักษณะทุบตีที่เรียกว่าการทดแทนคำสั่ง):

$(example_command)

ให้ลำดับฉันใช้ 1 ถึง 20 เป็นตัวอย่าง ในคำสั่งสุดท้ายคำสั่ง tput จะถูกใช้แทน 20:

seq 1 20

ระบุจำนวนคอลัมน์ที่ใช้ในเทอร์มินัลในปัจจุบัน:

tput cols




2

ทางเลือกที่หรูหรากว่าสำหรับโซลูชัน Python ที่เสนออาจเป็น:

python -c 'print "="*(1000)'

1

ในกรณีที่คุณต้องการทำซ้ำตัวอักษร n ครั้งเป็น na จำนวนครั้งที่หลากหลายขึ้นอยู่กับความยาวของสตริงที่คุณสามารถทำได้:

#!/bin/bash
vari='AB'
n=$(expr 10 - length $vari)
echo 'vari equals.............................: '$vari
echo 'Up to 10 positions I must fill with.....: '$n' equal signs'
echo $vari$(perl -E 'say "=" x '$n)

มันแสดง:

vari equals.............................: AB  
Up to 10 positions I must fill with.....: 8 equal signs  
AB========  

lengthจะไม่ทำงานด้วยexprคุณอาจหมายถึงn=$(expr 10 - ${#vari}); n=$(( 10 - ${#vari} ))แต่ก็เรียบง่ายและมีประสิทธิภาพมากขึ้นเพื่อใช้ขยายตัวทางคณิตศาสตร์ของทุบตี: นอกจากนี้ที่หลักของคำตอบของคุณเป็นอย่างมากวิธี Perl ว่า OP ที่กำลังมองหาทุบตี ทางเลือกที่จะ
mklement0

1

นี่เป็นเวอร์ชันที่ยาวกว่าของสิ่งที่ Eliah Kagan ได้รับมอบหมาย:

while [ $(( i-- )) -gt 0 ]; do echo -n "  "; done

แน่นอนคุณสามารถใช้ printf สำหรับสิ่งนั้นได้เช่นกัน แต่ก็ไม่ได้ตามความชอบของฉัน:

printf "%$(( i*2 ))s"

เวอร์ชันนี้รองรับ Dash:

until [ $(( i=i-1 )) -lt 0 ]; do echo -n "  "; done

โดยที่ฉันเป็นหมายเลขเริ่มต้น


ในทุบตีและด้วยค่าบวก n: while (( i-- )); do echo -n " "; doneทำงาน

1
function repeatString()
{
    local -r string="${1}"
    local -r numberToRepeat="${2}"

    if [[ "${string}" != '' && "${numberToRepeat}" =~ ^[1-9][0-9]*$ ]]
    then
        local -r result="$(printf "%${numberToRepeat}s")"
        echo -e "${result// /${string}}"
    fi
}

ตัวอย่างการวิ่ง

$ repeatString 'a1' 10 
a1a1a1a1a1a1a1a1a1a1

$ repeatString 'a1' 0 

$ repeatString '' 10 

อ้างอิง lib ที่: https://github.com/gdbtek/linux-cookbooks/blob/master/lไลบรารี/util.bash


1

ฉันจะทำสิ่งนี้ด้วยเสียงสะท้อนได้อย่างไร

คุณสามารถทำได้ด้วยechoหากechoมีการติดตามด้วยsed:

echo | sed -r ':a s/^(.*)$/=\1/; /^={100}$/q; ba'

จริงๆแล้วechoมันไม่จำเป็นเลย


1

คำตอบของฉันค่อนข้างซับซ้อนและอาจไม่สมบูรณ์แบบ แต่สำหรับผู้ที่ต้องการส่งออกจำนวนมากฉันสามารถทำประมาณ 10 ล้านใน 3 วินาที

repeatString(){
    # argument 1: The string to print
    # argument 2: The number of times to print
    stringToPrint=$1
    length=$2

    # Find the largest integer value of x in 2^x=(number of times to repeat) using logarithms
    power=`echo "l(${length})/l(2)" | bc -l`
    power=`echo "scale=0; ${power}/1" | bc`

    # Get the difference between the length and 2^x
    diff=`echo "${length} - 2^${power}" | bc`

    # Double the string length to the power of x
    for i in `seq "${power}"`; do 
        stringToPrint="${stringToPrint}${stringToPrint}"
    done

    #Since we know that the string is now at least bigger than half the total, grab however many more we need and add it to the string.
    stringToPrint="${stringToPrint}${stringToPrint:0:${diff}}"
    echo ${stringToPrint}
}


1

อีกตัวเลือกหนึ่งคือการใช้ GNU seq และลบหมายเลขและบรรทัดใหม่ทั้งหมดที่สร้าง:

seq -f'#%.0f' 100 | tr -d '\n0123456789'

คำสั่งนี้พิมพ์#ตัวอักษร 100 ครั้ง


1

โซลูชันที่มีอยู่ส่วนใหญ่ทั้งหมดขึ้นอยู่กับ{1..10}การสนับสนุนทางไวยากรณ์ของเชลล์ซึ่งเป็นbash- และzsh- เจาะจงและไม่ทำงานในtcshหรือ OpenBSD's kshและ non-bash ส่วนใหญ่shและส่วนใหญ่ไม่ใช่ทุบตี

ข้อมูลต่อไปนี้ควรใช้กับ OS X และระบบ * BSD ทั้งหมดในเชลล์ใด ๆ ในความเป็นจริงมันสามารถใช้ในการสร้างเมทริกซ์พื้นที่ตกแต่งประเภทต่างๆ:

$ printf '=%.0s' `jot 64` | fold -16
================
================
================
================$ 

น่าเศร้าที่เราไม่ได้ขึ้นบรรทัดใหม่ ซึ่งสามารถแก้ไขได้โดยการเสริมprintf '\n'หลังจากการพับ:

$ printf "=%.0s" `jot 64` | fold -16 ; printf "\n"
================
================
================
================
$ 

อ้างอิง:


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