我该如何使用 echo
来实现这个?
perl -E 'say "=" x 100'
你可以使用:
printf '=%.0s' {1..100}
工作原理:
Bash扩展{1..100}
,所以命令变成了:
printf '=%.0s' 1 2 3 4 ... 100
我将printf的格式设置为=%.0s
,这意味着无论给出什么参数,它都将始终打印一个单独的=
。 因此,它会打印100个=
。
repl = 100
调用它(不幸的是需要进行“eval”技巧,以便基于变量进行括号扩展):repl() { printf "$1"'%.s' $(eval "echo {1.."$(($2))"}"); }
- mklement0seq
命令代替,例如 $(seq 1 $limit)
。 - dogbane$s%.0s
重新排列为%.0s$s
,否则破折号会导致printf
出错。 - KomodoDaveprintf
的一个行为:它会一直应用格式字符串,直到没有参数为止。我以为它只处理一次格式字符串! - Jeenu没有简单的方法。但是举个例子:
seq -s= 100|tr -d '[:digit:]'
# Editor's note: This requires BSD seq, and breaks with GNU seq (see comments)
或者可能是符合标准的方式:
printf %100s |tr " " "="
还有一个 tput rep
,但是对于我手头的终端(xterm和linux),它们似乎不支持它 :)
printf
和 tr
是唯一符合 POSIX 标准的解决方案,因为 seq
、yes
和 {1..3}
不符合 POSIX 标准。 - Ciro Santilli OurBigBook.comprintf %100s | sed 's/ /abc/g'
- 输出 'abcabcabc...'。 - John Rixtr
),不使用循环语句实现加1操作。你也可以将它扩展到类似printf "%${COLUMNS}s\n" | tr " " "="
这样的内容。 - musiphilseq
的实现(因此也隐含了平台):GNU seq
(Linux) 会比指定的数字少一个 =
(与我最初所述的不同,但正如您正确地确定的那样),而 BSD seq
(包括OSX在内的类似BSD的平台) 会产生所需的数字。简单的测试命令:seq -s= 100 | tr -d '[:digit:]\n' | wc -c
。BSD seq
在_每个_数字后面放置=
,包括最后一个数字,而GNU seq在最后一个数字后面放置_换行符_,因此在=
计数方面短1。 - mklement0向@gniourf_gniourf致敬,感谢他的贡献。
注意:本答案并不回答原问题,而是通过“比较性能”来补充现有的有用答案。
解决方案仅从执行速度方面进行比较 - 不考虑内存需求(它们在解决方案之间变化,并且可能会影响大量重复计数)。
摘要:
${var// /=}
),因为它的速度非常慢。以下是在运行OSX 10.10.4和bash 3.2.57的3.2 GHz英特尔Core i5 CPU和Fusion Drive上进行的时间测量,平均值为1000次运行。
条目如下:
M
... 可能是多个字符解决方案S
... 仅限单个字符的解决方案P
... 符合POSIX标准的解决方案[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
[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
${foo// /=}
)对于大型字符串来说极其缓慢,且没有合理的解释,因此不能使用(在Bash 4.3.30中需要花费约50分钟(!),而在Bash 3.2.57中需要更长时间——我从未等待它完成)。(( i=0;...))
)比括号扩展的循环({1..n}
)速度更慢——虽然算术循环更加节省内存。awk
指的是BSDawk
(例如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
In order to use brace expansion with a variable, we must use \
eval`` - pyb*
时,我的当前目录文件列表的结果被获取到变量中,方法是执行以下操作:myvar=$(printf -- '*%.0s' {1..5})
。 - Ricky Levi$myvar
未引用 的情况下才会发生,此时可预见地发生_路径扩展_ (globbing)。使用 echo "$myvar"
查看 $myvar
本身是否以逐字表明的 ``***** 填充正确。 - mklement0有很多种方法可以实现。
使用循环:
花括号扩展可以与整数文字一起使用:
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次。
使用head
(printf
等)和tr
:
head -c 100 < /dev/zero | tr '\0' '='
printf %100s | tr " " "="
head
/tr
的解决方案而言,即使重复次数很高也能够良好运行(小缺陷是:head -c
不符合POSIX标准,但BSD和GNU的head
都实现了它);而另外两种解决方案在这种情况下会比较慢,但它们也有一个优点,就是能够处理多字符字符串。 - mklement0yes
和 head
-- 如果你想要一定数量的换行符,这个命令非常有用:yes "" | head -n 100
。tr
可以将任何字符打印出来:yes "" | head -n 100 | tr "\n" "="; echo
。 - loxaxsdd if=/dev/zero count=1 bs=100000000 | tr '\0' '=' >/dev/null
比 head -c100000000 < /dev/zero | tr '\0' '=' >/dev/null
版本慢得多。当然,您必须使用100M+的块大小才能合理地测量时间差异。 100M字节需要1.7秒和1秒,分别显示了两个版本。我去掉了tr并将其转储到 /dev/null
,对于十亿字节,head
版本为0.287秒,而dd
版本为0.675秒。 - Michael Goldshteyndd if=/dev/zero count=1 bs=100000000 | tr '\0' '=' >/dev/null
=> 0,21332 秒,469 MB/s
;针对:dd if=/dev/zero count=100 bs=1000000| tr '\0' '=' >/dev/null
=> 0,161579 秒,619 MB/s
; - 3EDprintf
/tr
打印标题和下划线:
CAPTION="测试套件结果" && echo "${CAPTION}" && printf "%${#CAPTION}s\n" | tr " " "=" && echo
在Mac和Linux上可以很好地工作。 - Martyn Davis我刚找到一个使用seq轻松完成此操作的方法:
更新:这在附带于 OS X 的 BSD seq
上可行。在其他版本中,请自行测试。
seq -f "#" -s '' 10
将会打印 '#' 十次,如下所示:
##########
-f "#"
将格式字符串设置为忽略数字,仅为每个数字打印#
。-s''
将分隔符设置为空字符串,以删除seq在每个数字之间插入的换行符-f
和-s
后面的空格似乎很重要。编辑:这是一个方便的函数...
repeat () {
seq -f $1 -s '' $2; echo
}
你可以这样调用...
repeat "#" 10
注意: 如果您要重复使用 #
,则引号很重要!
seq
是用于数字的,不是字符串。请参考 https://www.gnu.org/software/coreutils/manual/html_node/seq-invocation.html。 - John Bseq
被巧妙地重新用于复制“字符串”:传递给-f
的格式字符串通常用于格式化生成的“数字”,但在这里仅包含要复制的字符串,以便输出仅包含该字符串的副本。不幸的是,GNU seq
要求格式字符串中存在“数字格式”,这就是你看到的错误。 - mklement0"$1"
(双引号),这样您也可以传递诸如'*'
和带有嵌入式空格的字符串。最后,如果您想要能够使用%
,则必须将其_加倍_(否则seq
会认为它是格式规范的一部分,例如%f
); 使用"${1//%/%%}"
就可以解决这个问题。由于(如您所提到的)您正在使用_BSD_ seq
,因此这将在_BSD_类似的操作系统上通常工作(例如FreeBSD)-相比之下,它在Linux上不起作用,其中使用_GNU_ seq
。 - mklement0seq: invalid format string:
#'`。 - Ricky Levi以下是两种有趣的方法:
ubuntu@ubuntu:~$ yes = | head -10 | paste -s -d '' - ========== ubuntu@ubuntu:~$ yes = | head -10 | tr -d "\n" ==========ubuntu@ubuntu:~$
请注意这两种方法略有不同,paste
方法以一个新行结束,而 tr
方法不会。
paste
不可解释地需要 -d '\0'
来指定空分隔符,并且使用 -d ''
会失败 - -d '\0'
应该适用于所有符合 POSIX 标准的 paste
实现,实际上也可以在 GNU paste
中工作。 - mklement0yes | mapfile -n 100 -C 'printf = \#' -c 1
- bishoptime yes = | head -500 | paste -s -d '\0' -; time yes | mapfile -n 500 -C 'printf = \#' -c 1
。
然而,更重要的是:如果您已经使用printf
,那么您可以采用更简单、更有效的方法,即接受的答案:printf '%.s=' $(seq 500)
。 - mklement0没有简单的方法。可以使用printf
和替换来避免循环。
str=$(printf "%40s")
echo ${str// /rep}
# echoes "rep" 40 times.
repl = 100
,例如(不输出尾随的 \n
):repl() { local ts=$(printf "%${2}s"); printf %s "${ts// /$1}"; }
- mklement0printf -v str …
而不是 str=$(printf …)
来避免调用子shell。对于一个通用解决方案,我会使用 printf "%s" "${str// /rep}"
代替 echo
,因为 printf
更加健壮,并且不像 echo
那样在以 -
开头的字符串上出现错误。 - musiphil这个问题是关于如何使用 echo
来实现的:
echo -e ''$_{1..100}'\b='
这将完全像perl -E 'say "=" x 100'
一样工作,但仅使用echo
。
$_1
、$_2
或其他上百个变量有值,这将失败。 - John Kugelman一种纯Bash的方法,不使用eval
、子shell、外部工具或花括号扩展(即,您可以将要重复的数字存储在一个变量中):
如果给定一个扩展为(非负)数字的变量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 变量名
:使用该参数后,printf
命令将格式化后的字符串内容存储在变量 变量名
中,而非输出到标准输出流。printf
命令将根据传入的参数打印相应数量的空格。例如,printf '%*s' 42
将打印出 42 个空格。${变量// /$模式}
将被扩展为将 变量
中所有空格替换为 $模式
扩展后的字符串。此外,您还可以通过间接扩展的方式,省去 repeat
函数中的 tmp
变量:
repeat() {
# $1=number of patterns to repeat
# $2=pattern
# $3=output variable name
printf -v "$3" '%*s' "$1"
printf -v "$3" '%s' "${!3// /$2}"
}
bash
在参数扩展($ {var // old / new}
)的全局字符串替换操作方面特别慢:在我的OSX 10.10.3系统上,bash 3.2.57
非常缓慢,而bash 4.3.30
则较慢,至少在我的3.2 GHz英特尔Core i5机器上。当计数为1,000时,速度很慢(3.2.57
)/快(4.3.30
):0.1 / 0.004秒。将计数增加到10,000会产生截然不同的数字:repeat 10000 = var
在bash 3.2.57
中需要大约80秒!在bash 4.3.30
中只需要大约0.3秒(比3.2.57
快得多,但仍然很慢)。 - mklement0#!/usr/bin/awk -f
BEGIN {
OFS = "="
NF = 100
print
}
或者
#!/usr/bin/awk -f
BEGIN {
while (z++ < 100) printf "="
}
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
中的一个错误,即传递以=
开头的变量值会破坏命令的问题。) - mklement0original-awk
是Linux下类似于BSD awk的旧版awk的名称,也有报道称其会崩溃。如果您想尝试,请注意崩溃通常是发现可利用漏洞的第一步。这个答案在促进不安全的代码。 - user2350426original-awk
是非标准的,不建议使用。 - Zombobash
和 gawk
的命令 awk NF=100 OFS='=' <<< ""
。 - oliv
ruby -e'puts"="*100'
或python -c'print"="*100'
- Evgenyprintf
和seq
实现)svrb = \
printf'%.sv' $(seq $vrb)`` - DrBecoPerl
对我来说已经足够好了。尝试了几个答案,但它们都在字符串末尾有一个%
,不知道为什么。 - DeqingPerl -E 'print "=" x 100'
。 - Deqing