如何在Bash中重复一个角色?


240

我该怎么办echo

perl -E 'say "=" x 100'

不幸的是,这不是Bash。
solidsnack

1
不是回声,而是在同一主题上ruby -e 'puts "=" * 100'python -c 'print "=" * 100'
Evgeny

1
好问题。很好的答案。我使用的答案之一,一个真正的工作在这里,我会张贴为例:github.com/drbeco/oldfiles/blob/master/oldfiles(使用printfseqsvrb=`printf '%.sv' $(seq $vrb)`
贝乔博士

打印任何内容(1个或多个字符,甚至包括换行符)的通用解决方案:Repeat_this(){i = 1; 而[“ $ i” -le“ $ 2”]; 做printf“%s”“ $ 1”; i = $((($ i + 1)); 完成; printf'\ n';}。像这样使用:Repeat_this“某物” Number_of_repetitions。例如,要展示重复5次的内容,其中包括3条换行符:Repeat_this“ $(printf'\ n \ n \ nthis')” 5。最终的printf'\ n'可能会被取出(但是我把它放进去创建文本文件,而这些文件最后一个字符需要换行!)
Olivier Dulac

Answers:


396

您可以使用:

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

工作原理:

Bash展开{1..100},因此命令变为:

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

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


14
即使重复次数很多,出色的解决方案也可以表现良好。例如,这是一个您可以使用调用的函数包装器repl = 100eval不幸的是,为了使括号扩展基于变量repl() { printf "$1"'%.s' $(eval "echo {1.."$(($2))"}"); }
技巧才能实现

7
是否可以使用var设置上限?我已经尝试过并且无法正常工作。
Mike Purcell 2014年

70
在括号扩展中不能使用变量。使用seq代替例如$(seq 1 $limit)
dogbane 2014年

11
如果您对此进行功能化,最好将其从重新排列为$s%.0s%.0s$s否则破折号会导致printf错误。
KomodoDave 2014年

5
这让我注意到了Bash的行为printf:它将继续应用格式字符串,直到没有剩余参数为止。我以为它只处理一次格式字符串!
Jeenu'1

89

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

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

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

printf %100s |tr " " "="

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


3
请注意,带有seq的第一个选项比给定的数字少打印一个,因此该示例将打印99个=字符。
卡米洛·马丁

13
printf tr是唯一的POSIX解决方案,因为seqyes{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好吧,我希望您在错误地计算了最后一个换行符wc。我唯一可以得出的结论是“ seq不应该使用”。
卡米洛·马丁

51

向@ gniourf_gniourf表示感谢。

注意:此答案回答原始问题,但补充由现有的,有用的答案比较性能

解决方案是 在执行速度方面比较 - 考虑内存需求(它们在解决方案中会有所不同,并且可能与大重复次数有关)。

摘要:

  • 如果您的重复次数很少,例如最多100次,那么值得仅Bash的解决方案,因为外部实用程序(尤其是Perl)的启动成本很重要。
    • 务实地说,如果您只需要一个重复字符的实例,那么所有现有的解决方案都可以。
  • 随着大量重复计数使用外部公共事业,因为他们会快很多。
    • 特别是,避免使用大字符串替换Bash的全局子字符串
      (例如,${var// /=}),因为这太慢了。

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

条目是:

  • 按执行时间的升序排列(最快的优先)
  • 前缀:
    • M...潜在的多重字符解决方案
    • S...一个只字符数限制的解决方案
    • 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(100万)
[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// /=})在使用大字符串时令人费解地缓慢,并且已经退出运行(在Bash 4.3.30中花费了大约50分钟(!),在Bash 3.2.57中甚至更长的时间-我从未等待完成)。
  • Bash循环较慢,算术循环((( i= 0; ... )))比括号扩展的({1..n}) -尽管算术循环的内存效率更高。
  • awk指的是BSD awk(也可以在OSX上找到)-明显比gawk(GNU Awk)慢,尤其是mawk
  • 注意,大量和多字符。字符串,内存消耗可以成为一个考虑因素-方式在这方面有所不同。

这是产生以上内容的Bash脚本testrepeat)。它有两个参数:

  • 字符重复计数
  • (可选)要执行的测试运行次数,以及从中计算平均计时的次数

换句话说:上述计时是通过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的初始开销,perl解决方案(sid_com)基本上是最快的。(它从一小段重复的59ms变为一百万次重复的67ms ...因此,perl分叉在您的系统上花费了大约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

使用printf内置的:

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

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

使用headprintf,等等)和tr

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

2
head/ tr解决方案的++,即使在重复次数很高的情况下也能很好地工作(小的警告:head -c不符合POSIX,但是BSD和GNU都head实现了它);尽管其他两种解决方案在这种情况下会很慢,但它们的确也具有使用字符字符串的优势。
mklement0 2015年

1
使用yeshead-有用的,如果你想一定数量的新行的:yes "" | head -n 100tr可以使其打印任何字符: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字节分别耗时1.7 s和1 s,分别显示了两个版本。我摘下了tr,然后将其转储到该版本中,得到十亿字节的版本为/dev/null0.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附带的BSD。YMMV和其他版本

seq  -f "#" -s '' 10

将打印“#”十次,如下所示:

##########
  • -f "#" 设置格式字符串以忽略数字并仅打印 #每个。
  • -s '' 将分隔符设置为空字符串以删除seq在每个数字之间插入的换行符
  • 该空间后-f,并-s似乎是很重要的。

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

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

你可以这样称呼...

repeat "#" 10

注意:如果要重复,#则引号很重要!


7
这给了我seq: format ‘#’ has no % directiveseq用于数字,而不是字符串。参见gnu.org/software/coreutils/manual/html_node/seq-invocation.html
John B

嗯,所以我使用的是OS X上的seq的BSD版本。我将更新答案。您正在使用哪个版本?
山姆·索尔兹伯里

我正在使用GNU coreutils中的seq。
约翰B

1
@JohnB:BSD 在这里seq巧妙地用于复制字符串:传递给-f-通常用于格式化生成的数字的格式字符串-仅包含要在此处复制的字符串,因此输出仅包含该字符串的副本。不幸的是,GNU seq坚持在格式字符串中存在数字格式,这是您看到的错误。
mklement0 2015年

1
做得很好; 也适用于字符字符串。请使用"$1"(双引号),因此您也可以传入'*'带有嵌入式空格的字符,例如和字符串。最后,如果您想使用%,则必须将其加倍(否则seq会认为它是格式规范的一部分,例如%f);使用"${1//%/%%}"会照顾的。由于(如您所述)您正在使用BSD seq,因此它通常可在类似BSD的OS(例如FreeBSD)上运行-相比之下,它将在使用GNU的Linux上不起作用 seq
mklement0 2015年

18

这是两种有趣的方式:

ubuntu @ ubuntu:〜$是= | 头-10 | 粘贴-s -d''-
==========
ubuntu @ ubuntu:〜$是= | 头-10 | tr -d“ \ n”
========== ubuntu @ ubuntu:〜$ 

请注意,这两者是有细微差别的- paste方法以新行结尾。该tr方法没有。


1
做得很好; 请注意,BSD paste莫名其妙地要求-d '\0'指定一个空的定界符,并且失败-d ''- -d '\0'应该与所有POSIX兼容的paste实现一起工作,并且实际上也与GNU一起 工作paste
mklement0 2015年

精神上相似,但舷外工具更少:yes | mapfile -n 100 -C 'printf = \#' -c 1
主教

@bishop:虽然您的命令确实创建了一个更少的子shell,但是对于较高的重复计数而言,它仍然较慢,对于较低的重复计数而言,差异可能并不重要;确切的阈值可能取决于硬件和操作系统,例如,在我的OSX 10.11.5机器上,该答案已经快了500。尝试一下time yes = | head -500 | paste -s -d '\0' -; time yes | mapfile -n 500 -C 'printf = \#' -c 1。但是,更重要的是:如果您printfprintf '%.s=' $(seq 500)
仍在使用

13

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

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

2
不错,但是仅在重复次数较少的情况下才能表现合理。例如,这是一个可以作为调用的函数包装器repl = 100(不输出结尾\n):repl() { local ts=$(printf "%${2}s"); printf %s "${ts// /$1}"; }
mklement0 2013年

1
@ mklement0很高兴为您提供两个解决方案的功能版本,两个都为+1!
卡米洛·马丁

8

如果您希望POSIX以及echoprintf和/或Shell的不同实现之间的一致性和一致性,而不仅仅是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系统具有它的实现)-您可以使用while循环来执行POSIX Shell算术,如@ Xennex81的答案(使用printf "=",如您正确建议的那样,而不是echo -n)。
mklement0 2015年

1
糟糕,您说得对。诸如此类的事情有时会从我身边溜走,因为那种标准毫无意义。cal是POSIX。seq不是。无论如何,我将添加一个RYO函数,而不是使用while循环重写答案(如您所说,这已经在其他答案中了)。这样更具教育意义;-)。
杰夫·尼克松

8

问题是关于如何使用echo

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

这将perl -E 'say "=" x 100'echo仅执行相同的操作。


现在这很不寻常,如果您不在其中添加多余的空格或..或使用以下命令清除它:echo -e $ _ {1..100}'\ b ='| 上校
安东尼

1
馊主意。如果$_1$_2或其他数百个变量具有值,则此操作将失败。
约翰·库格曼

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

太过分了。我喜欢它:D
dimo414

6

没有eval,没有子shell,没有外部工具,没有括号扩展的纯Bash方式(即,您可以在变量中重复数字):

如果您得到一个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 varnameprintf将格式字符串的内容放入变量中,而不是打印到标准输出中varname
  • '%* 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}"
}

传递变量名称的有趣变化。虽然此解决方案适用于最多1000次左右的重复计数(因此,如果我猜是可能的话,可能适用于大多数实际应用),但对于较高的计数却非常慢(请参阅下一个)评论)。
mklement0 2015年

似乎bash在参数扩展(${var//old/new})的上下文中的全局字符串替换操作特别慢:至少在3.2 GHz英特尔酷睿i5机器上的OSX 10.10.3系统上,bash的运行速度极其缓慢,bash的运行3.2.57速度极慢4.3.30:计数为1,000,则慢速(3.2.57)/快速(4.3.30):0.1 / 0.004秒。将计数增加到10,000会产生截然不同的数字: repeat 10000 = varbash大约需要80秒(!),bash 3.2.57大约需要0.3秒4.3.30(比on快得多3.2.57,但仍然很慢)。
mklement0 2015年

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 "=" }'。裹成参数化shell函数(调用如repeat 100 =,例如): repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { txt=substr(txt, 2); while (i++ < count) printf txt }'; }。(虚设.前缀炭和互补substr呼叫需要以围绕工作在BSD的错误awk,其中,传递变量值,该值开始=断命令。)
mklement0

1
NF = 100解决方案是很聪明的(虽然得到100 =,则必须使用NF = 101)。需要注意的是,它使BSD崩溃awk(但使用会很快,gawk甚至更快mawk),并且POSIX既没有讨论分配NF,也没有讨论在BEGIN块中使用字段。您也可以通过awk稍作调整使其在BSD中工作:(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类似BSD的AWK,这也被报道崩溃,如果你想尝试这种旧的awk Linux下的名称。请注意,崩溃通常是查找可利用错误的第一步。这个答案是如此促进不安全的代码。

2
使用者注意- original-awk是非标准且不建议使用
史蒂文·彭尼

第一个代码段的替代方法是awk NF=100 OFS='=' <<< ""(使用bashgawk
oliv '18

4

我想这个问题的最初目的是仅使用Shell的内置命令来执行此操作。因此,for循环和printfs就合法,同时repperl和也jot低于不肯。尽管如此,以下命令

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

例如,在 \/\/\/\/\/\/\/\/\/\/\/\/


2
做得很好; 即使重复次数很高(也支持多字符字符串),这也很好。为了更好地说明这种方法,这里的相当于OP的命令:jot -s '' -b '=' 100。需要注意的是,虽然附带了包括OSX在内的类似BSD的平台jot,但Linux发行版却没有
mklement0 2015年

1
谢谢,我更喜欢您使用-s”。我已经更改了脚本。
Stefan Ludwig

在最近的基于Debian的系统上,apt install athena-jot将提供jot
agc

4

正如其他人所说,在bash中,括号扩展参数扩展之前,因此范围只能包含文字。并提供干净的解决方案,但即使在每个系统上使用相同的外壳,也不能完全移植到一个系统中。(尽管越来越多;例如,在FreeBSD 9.3及更高版本中。){m,n}seqjotseqeval和其他形式的间接访问始终有效,但有些不雅。

幸运的是,bash 支持C风格的循环(仅适用于算术表达式)。因此,这是一种简洁的“纯bash”方式:

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

这将重复次数作为第一个参数,将要重复的字符串(如问题描述中的单个字符)作为第二个参数。repecho 7 b输出bbbbbbb(由换行符终止)。

丹尼斯·威廉姆森Dennis Williamson)四年前对在shell脚本中创建重复字符字符串的出色回答中就给出了这种解决方案。我的函数体与那里的代码略有不同:

  • 由于此处的重点是重复单个字符且shell是bash,因此使用echo代替可能是安全的printf。而且我读到该问题的问题描述是表示倾向于使用echo。上面的函数定义在bash和ksh93中起作用。尽管它printf具有更高的可移植性(通常应用于此类操作),但echo可以说的语法更具可读性。

    一些shell的echo内建函数-本身将其解释为一个选项-尽管-使用stdin作为输入的通常含义对而言是毫无意义的echozsh做到了。而且肯定存在echo无法识别的-n,因为它不是标准的。(许多Bourne风格的shell根本不接受C风格的循环,因此echo不需要考虑它们的行为。)

  • 这里的任务是打印序列。在那里,是将其分配给变量。

如果$n期望的重复次数是,则不必重复使用它,而您想要的则更短一些:

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

n必须为变量-这种方式不适用于位置参数。$s是要重复的文本。


2
强烈避免执行循环版本。printf "%100s" | tr ' ' '='是最佳的。
ocodo 2014年

良好的背景信息和将功能打包为功能的荣誉,也可以zsh同时使用。循环中的回声方法对于较小的重复计数效果很好,但是对于较大的重复计数,有基于实用程序的 POSIX兼容替代方法,如@Slomojo的评论所证明。
mklement0 2015年

在较短的循环周围添加括号可保留n的值,而不会影响回波:(while ((n--)); do echo -n "$s"; done; echo)

使用printf代替echo!它更加便携(echo -n只能在某些系统上工作)。参见unix.stackexchange.com/questions/65803/…(Stephane Chazelas的出色回答之一)
Olivier Dulac

@OlivierDulac这里的问题是关于bash的。无论您运行的是哪种操作系统,如果您在上面使用bash,bash都有一个echo内置的support -n。您所说的精神是绝对正确的。至少在非交互使用中,printf应该几乎总是首选echo。但是我认为echo对一个提出问题的答案给出任何答案并提供足够的信息以知道它会起作用是不适当的或具有误导性。另请注意,POSIX本身不保证支持((n--))(不带$)。
伊利亚·卡根

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

在bash 3.0或更高版本中

for i in {1..100};do echo -n =;done

3

重复任意字符串n次的另一种方法是:

优点:

  • 与POSIX shell一起使用。
  • 可以将输出分配给变量。
  • 重复任何字符串。
  • 即使重复很大,也非常快。

缺点:

  • 需要Gnu Core Utils的yes命令。
#!/usr/bin/sh
to_repeat='='
repeat_count=80
yes "$to_repeat" | tr -d '\n' | head -c "$repeat_count"

用ANSI终端和US-ASCII字符重复。您可以使用ANSI CSI转义序列。这是重复字符的最快方法。

#!/usr/bin/env bash

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

或静态地:

打印80行=

printf '=\e[80b\n'

局限性:

  • 并非所有终端都能理解repeat_charANSI CSI序列。
  • 只能重复使用US-ASCII或单字节ISO字符。
  • 在最后一列重复停止,因此无论端子宽度如何,都可以使用较大的值来填充整行。
  • 重复仅用于显示。将输出捕获到shell变量中不会将repeat_charANSI CSI序列扩展为重复的字符。

1
小注释-如果终端处于自动包装模式,则REP(CSI b)应该正常包装。
jerch

3

这是我在linux中跨屏幕打印一行字符的方法(基于终端/屏幕宽度)

在屏幕上打印“ =”:

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

说明:

按给定序列打印等号多次:

printf '=%.0s' #sequence

使用命令的输出(这是一个称为Command Substitution的bash功能):

$(example_command)

给一个序列,我以1到20为例。在最终命令中,使用tput命令代替20:

seq 1 20

给出终端当前使用的列数:

tput cols


2
repeat() {
    # $1=number of patterns to repeat
    # $2=pattern
    printf -v "TEMP" '%*s' "$1"
    echo ${TEMP// /$2}
}

2

最简单的方法是在csh / tcsh中使用此单行代码:

printf "%50s\n" '' | tr '[:blank:]' '[=]'



1

如果您想重复一个字符n次为n个可变次数(例如取决于字符串的长度),则可以执行以下操作:

#!/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========  

lengthexpr您可能会表示不予合作n=$(expr 10 - ${#vari}); 但是,使用Bash的算术扩展更为简单高效n=$(( 10 - ${#vari} ))。另外,您的答案的核心是OP正在寻找Bash 替代品的 Perl方法 。
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

我是初始号码。


以bash且正数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 

参考库位于:https : //github.com/gdbtek/linux-cookbooks/blob/master/libraries/util.bash


1

我该如何用echo做到这一点?

echo如果echo后面跟着,您可以执行以下操作sed

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

实际上,那echo是不必要的。


1

我的答案有点复杂,可能还不完美,但是对于那些希望输出大量数字的人来说,我能够在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

最简单的方法是在bash中使用这种单线:

seq 10 | xargs -n 1 | xargs -I {} echo -n  ===\>;echo


1

另一个选择是使用GNU seq并删除它生成的所有数字和换行符:

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

此命令将#字符打印100次。


1

大多数现有的解决方案都依赖于{1..10}语法的支持壳,这是bash-和zsh-特定的,并且不工作tcsh或OpenBSD的ksh和大多数非的bashsh

以下内容适用于OS X和任何外壳中的所有* BSD系统;实际上,它可以用于生成各种装饰空间的整体矩阵:

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

可悲的是,我们没有换行符。printf '\n'折叠后可以额外固定:

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

参考文献:


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.