如何在 Bash 中重复一个字符?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5349718/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 20:18:11  来源:igfitidea点击:

How can I repeat a character in Bash?

bashshellecho

提问by sid_com

How could I do this with echo?

我怎么能做到这一点echo

perl -E 'say "=" x 100'

回答by dogbane

You can use:

您可以使用:

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

How this works:

这是如何工作的:

Bash expands {1..100} so the command becomes:

Bash 扩展 {1..100} 所以命令变成:

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

I've set printf's format to =%.0swhich means that it will always print a single =no matter what argument it is given. Therefore it prints 100 =s.

我已经将 printf 的格式设置为=%.0s这意味着=无论给出什么参数,它都将始终打印一个。因此它打印 100=秒。

回答by sid_com

No easy way. But for example:

没有简单的方法。但例如:

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

Or maybe a standard-conforming way:

或者也许是一种符合标准的方式:

printf %100s |tr " " "="

There's also a tput rep, but as for my terminals at hand (xterm and linux) they don't seem to support it:)

还有一个tput rep, 但至于我手头的终端(xterm 和 linux),他们似乎不支持它:)

回答by mklement0

Tip of the hat to @gniourf_gniourffor his input.

尖帽子@的gniourf_gniourf为他输入。

Note: This answer does notanswer the original question, but complementsthe existing, helpful answers by comparing performance.

注意:此答案并未回答原始问题,而是通过比较 performance 来补充现有的有用答案。

Solutions are compared in terms of execution speed only- memory requirements are nottaken into account (they vary across solutions and may matter with large repeat counts).

在执行速度方面比较解决方案-考虑内存要求(它们因解决方案而异,并且可能与大量重复计数有关)。

Summary:

概括:

  • If your repeat count is small, say up to around 100, it's worth going with the Bash-only solutions, as the startup cost of external utilities matters, especially Perl's.
    • Pragmatically speaking, however, if you only need oneinstance of repeating characters, all existing solutions may be fine.
  • With largerepeat counts, use external utilities, as they'll be much faster.
    • In particular, avoid Bash's global substring replacement with large strings
      (e.g., ${var// /=}), as it is prohibitively slow.
  • 如果您的重复次数很少,比如大约 100 次,那么使用Bash-only 解决方案值得的,因为外部实用程序的启动成本很重要,尤其是 Perl 的。
    • 但是,从实用的角度来说,如果您只需要一个重复字符的实例,那么所有现有的解决方案都可以。
  • 对于大量重复计数,请使用外部实用程序,因为它们会快得多。
    • 特别要避免 Bash 用大字符串
      (例如,${var// /=})替换全局子字符串,因为它太慢了。

The following are timingstaken on a late-2012 iMac with a 3.2 GHz Intel Core i5 CPU and a Fusion Drive, running OSX 10.10.4 and bash 3.2.57, and are the average of 1000 runs.

以下是时机了3.2 GHz的英特尔酷睿i5 CPU和融合驱动器,运行OSX 10.10.4和bash 3.2.57在后期2012的iMac采取,并且是平均1000个奔跑。

The entries are:

条目是:

  • listed in ascending order of execution duration (fastest first)
  • prefixed with:
    • M... a potentially multi-character solution
    • S... a single-character-only solution
    • P... a POSIX-compliant solution
  • followed by a brief description of the solution
  • suffixed with the name of the author of the originating answer
  • 按执行时长升序列出(最快的在前)
  • 前缀为:
    • M... 一个潜在的字符解决方案
    • S...字符解决方案
    • P... 符合 POSIX 标准的解决方案
  • 然后是解决方案的简要说明
  • 以原始答案的作者姓名为后缀


  • Small repeat count: 100
  • 小重复次数: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
  • The Bash-only solutions lead the pack - but only with a repeat count this small! (see below).
  • Startup cost of external utilities does matter here, especially Perl's. If you must call this in a loop - with smallrepetition counts in each iteration - avoid the multi-utility, awk, and perlsolutions.
  • Bash-only 解决方案领先 - 但只有这么小的重复计数!(见下文)。
  • 外部实用程序的启动成本在这里很重要,尤其是 Perl 的。如果您必须在循环中调用它 -每次迭代中的重复次数很少- 避免多效用awk、 和perl解决方案。


  • Large repeat count: 1000000 (1 million)
  • 大重复计数:1000000(百万)
[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
  • The Perl solution from the question is by far the fastest.
  • Bash's global string-replacement (${foo// /=}) is inexplicably excruciatingly slow with large strings, and has been taken out of the running (took around 50 minutes(!) in Bash 4.3.30, and even longer in Bash 3.2.57 - I never waited for it to finish).
  • Bash loops are slow, and arithmetic loops ((( i= 0; ... ))) are slower than brace-expanded ones ({1..n}) - though arithmetic loops are more memory-efficient.
  • awkrefers to BSDawk(as also found on OSX) - it's noticeably slower than gawk(GNU Awk) and especially mawk.
  • Note that with large counts and multi-char. strings, memory consumption can become a consideration - the approaches differ in that respect.
  • 问题中的 Perl 解决方案是迄今为止最快的。
  • Bash 的全局字符串替换 ( ${foo// /=}) 使用大字符串时莫名其妙地慢得令人难以忍受,并且在 Bash 4.3.30 中花费了大约 50 分钟(!)它完成)。
  • Bash 循环很慢,算术循环 ( (( i= 0; ... ))) 比大括号扩展循环 ( ) 慢{1..n}——尽管算术循环更节省内存。
  • awk指的是BSDawk(也可以在 OSX 上找到)-它明显比gawk(GNU Awk)慢,尤其是mawk.
  • 请注意,对于大计数和多字符。字符串,内存消耗可以成为一个考虑因素 - 方法在这方面有所不同。


Here's the Bash script(testrepeat) that produced the above. It takes 2 arguments:

这是生成上述内容的Bash 脚本( testrepeat)。它需要 2 个参数:

  • the character repeat count
  • optionally, the number of test runs to perform and to calculate the averagetiming from
  • 字符重复计数
  • 可选地,要执行的测试运行次数并计算平均时间

In other words: the timings above were obtained with testrepeat 100 1000and testrepeat 1000000 1000

换句话说:上面的时间是通过testrepeat 100 1000testrepeat 1000000 1000

#!/usr/bin/env bash

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

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 '
for i in {1..100}; do echo -n =; done    
' '=' >"$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 '
start=1
end=100
for ((i=$start; i<=$end; i++)); do echo -n =; done
' "=" >"$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", ; if ( ~ "^n/a") { print } else { printf "%.4f\n", / count }}' | column -s$'\t' -t

回答by Eugene Yarmash

There's more than one way to do it.

有不止一种方法可以做到。

Using a loop:

使用循环:

  • Brace expansion can be used with integer literals:

    for i in {1..100}; do echo -n =; done    
    
  • A C-like loop allows the use of variables:

    start=1
    end=100
    for ((i=$start; i<=$end; i++)); do echo -n =; done
    
  • 大括号扩展可以与整数文字一起使用:

    printf '=%.0s' {1..100}
    
  • 类似 C 的循环允许使用变量:

    head -c 100 < /dev/zero | tr '
    seq  -f "#" -s '' 10
    
    ' '=' printf %100s | tr " " "="

Using the printfbuiltin:

使用printf内置:

##########

Specifying a precision here truncates the string to fit the specified width (0). As printfreuses the format string to consume all of the arguments, this simply prints "="100 times.

在此处指定精度会截断字符串以适合指定的宽度 ( 0)。由于printf重用格式字符串来使用所有参数,这只会打印"="100 次。

Using head(printf, etc) and tr:

使用head(printf等) 和tr

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

回答by Sam Salisbury

I've just found a seriously easy way to do this using seq:

我刚刚找到了一种非常简单的方法来使用 seq:

UPDATE: This works on the BSD seqthat comes with OS X. YMMV with other versions

更新:这适用于seqOS X 附带的 BSD。YMMV 与其他版本

repeat "#" 10

Will print '#' 10 times, like this:

将打印 '#' 10 次,如下所示:

ubuntu@ubuntu:~$ yes = | head -10 | paste -s -d '' -
==========
ubuntu@ubuntu:~$ yes = | head -10 | tr -d "\n"
==========ubuntu@ubuntu:~$ 
  • -f "#"sets the format string to ignore the numbers and just print #for each one.
  • -s ''sets the separator to an empty string to remove the newlines that seq inserts between each number
  • The spaces after -fand -sseem to be important.
  • -f "#"将格式字符串设置为忽略数字并只打印#每个数字。
  • -s ''将分隔符设置为空字符串以删除 seq 在每个数字之间插入的换行符
  • -f和之后的空格-s似乎很重要。

EDIT: Here it is in a handy function...

编辑:这是一个方便的功能......

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

Which you can call like this...

你可以这样称呼...

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

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

NOTE:If you're repeating #then the quotes are important!

注意:如果您要重复,#那么引号很重要!

回答by Digital Trauma

Here's two interesting ways:

这里有两个有趣的方法:

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

Note these two are subtly different - The pastemethod ends in a new line. The trmethod does not.

请注意,这两者略有不同 - 该paste方法以新行结束。该tr方法没有。

回答by Tim

There is no simple way. Avoid loops using printfand substitution.

没有简单的方法。避免循环使用printf和替换。

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

回答by Geoff Nixon

If you want POSIX-compliance and consistency across different implementations of echoand printf, and/or shells other than just bash:

如果您希望在echoandprintf和/或 shell 的不同实现之间实现 POSIX 合规性和一致性,而不仅仅是bash

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

...will produce the same output as perl -E 'say "=" x 100'just about everywhere.

...将产生与perl -E 'say "=" x 100'几乎所有地方相同的输出。

回答by manifestor

The question was about how to do it with echo:

问题是如何做到这一点echo

##代码##

This will will do exactly the same as perl -E 'say "=" x 100'but with echoonly.

这将与perl -E 'say "=" x 100'但与echoonly完全相同。

回答by Steven Penny

##代码##

Or

或者

##代码##

Example

例子