How can I repeat a character in Bash?
How could I do this with echo
?
perl -E 'say "=" x 100'
bash shell echo
add a comment |
How could I do this with echo
?
perl -E 'say "=" x 100'
bash shell echo
1
possible duplicate of shell script create string of repeated characters
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Apr 10 '14 at 10:54
Sadly this is not Bash.
– solidsnack
Feb 20 '16 at 20:42
1
not with echo, but on the same topicruby -e 'puts "=" * 100'
orpython -c 'print "=" * 100'
– Evgeny
Apr 20 '17 at 15:53
1
Great question. Very good answers. I've used one of the answers in a real job here, that I'll post as an example: github.com/drbeco/oldfiles/blob/master/oldfiles (usedprintf
withseq
)svrb=`printf '%.sv' $(seq $vrb)`
– Dr Beco
Jul 7 '17 at 5:21
add a comment |
How could I do this with echo
?
perl -E 'say "=" x 100'
bash shell echo
How could I do this with echo
?
perl -E 'say "=" x 100'
bash shell echo
bash shell echo
edited Feb 1 at 13:13


Mateusz Piotrowski
3,95063152
3,95063152
asked Mar 18 '11 at 8:45
sid_comsid_com
9,3021879152
9,3021879152
1
possible duplicate of shell script create string of repeated characters
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Apr 10 '14 at 10:54
Sadly this is not Bash.
– solidsnack
Feb 20 '16 at 20:42
1
not with echo, but on the same topicruby -e 'puts "=" * 100'
orpython -c 'print "=" * 100'
– Evgeny
Apr 20 '17 at 15:53
1
Great question. Very good answers. I've used one of the answers in a real job here, that I'll post as an example: github.com/drbeco/oldfiles/blob/master/oldfiles (usedprintf
withseq
)svrb=`printf '%.sv' $(seq $vrb)`
– Dr Beco
Jul 7 '17 at 5:21
add a comment |
1
possible duplicate of shell script create string of repeated characters
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Apr 10 '14 at 10:54
Sadly this is not Bash.
– solidsnack
Feb 20 '16 at 20:42
1
not with echo, but on the same topicruby -e 'puts "=" * 100'
orpython -c 'print "=" * 100'
– Evgeny
Apr 20 '17 at 15:53
1
Great question. Very good answers. I've used one of the answers in a real job here, that I'll post as an example: github.com/drbeco/oldfiles/blob/master/oldfiles (usedprintf
withseq
)svrb=`printf '%.sv' $(seq $vrb)`
– Dr Beco
Jul 7 '17 at 5:21
1
1
possible duplicate of shell script create string of repeated characters
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Apr 10 '14 at 10:54
possible duplicate of shell script create string of repeated characters
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Apr 10 '14 at 10:54
Sadly this is not Bash.
– solidsnack
Feb 20 '16 at 20:42
Sadly this is not Bash.
– solidsnack
Feb 20 '16 at 20:42
1
1
not with echo, but on the same topic
ruby -e 'puts "=" * 100'
or python -c 'print "=" * 100'
– Evgeny
Apr 20 '17 at 15:53
not with echo, but on the same topic
ruby -e 'puts "=" * 100'
or python -c 'print "=" * 100'
– Evgeny
Apr 20 '17 at 15:53
1
1
Great question. Very good answers. I've used one of the answers in a real job here, that I'll post as an example: github.com/drbeco/oldfiles/blob/master/oldfiles (used
printf
with seq
) svrb=`printf '%.sv' $(seq $vrb)`
– Dr Beco
Jul 7 '17 at 5:21
Great question. Very good answers. I've used one of the answers in a real job here, that I'll post as an example: github.com/drbeco/oldfiles/blob/master/oldfiles (used
printf
with seq
) svrb=`printf '%.sv' $(seq $vrb)`
– Dr Beco
Jul 7 '17 at 5:21
add a comment |
23 Answers
23
active
oldest
votes
You can use:
printf '=%.0s' {1..100}
How this works:
Bash expands {1..100} so the command becomes:
printf '=%.0s' 1 2 3 4 ... 100
I've set printf's format to =%.0s
which means that it will always print a single =
no matter what argument it is given. Therefore it prints 100 =
s.
11
Great solution that performs reasonably well even with large repeat counts. Here's a function wrapper you can invoke withrepl = 100
, for instance (eval
trickery is required, unfortunately, for basing the brace expansion on a variable):repl() { printf "$1"'%.s' $(eval "echo {1.."$(($2))"}"); }
– mklement0
Dec 7 '13 at 21:34
4
Is it possible to set the upper limit using a var? I've tried and can't get it to work.
– Mike Purcell
Jan 10 '14 at 20:30
54
You can't use variables within brace expansion. Useseq
instead e.g.$(seq 1 $limit)
.
– dogbane
Jan 11 '14 at 8:22
10
If you functionalise this it's best to rearrange it from$s%.0s
to%.0s$s
otherwise dashes cause aprintf
error.
– KomodoDave
Jul 30 '14 at 7:35
2
This made me notice a behaviour of Bash'sprintf
: it continues to apply the format string until there are no arguments left. I had assumed it processed the format string only once!
– Jeenu
Jan 8 '15 at 10:25
|
show 11 more comments
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:)
2
Note that the first option with seq prints one less than the number given, so that example will print 99=
characters.
– Camilo Martin
Jan 2 '14 at 16:10
11
printf
tr
is the only POSIX solution becauseseq
,yes
and{1..3}
are not POSIX.
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Apr 10 '14 at 11:02
2
To repeat a string rather than just a single character:printf %100s | sed 's/ /abc/g'
- outputs 'abcabcabc...'
– John Rix
Sep 11 '14 at 12:51
1
+1 for using no loops and only one external command (tr
). You could also extend it to something likeprintf "%${COLUMNS}sn" | tr " " "="
.
– musiphil
Mar 16 '15 at 20:59
2
@mklement0 Well, I was hoping you were counting the last newline by mistake withwc
. The only conclusion I can take from this is "seq
shouldn't be used".
– Camilo Martin
May 3 '15 at 20:17
|
show 5 more comments
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
Using the printf
builtin:
printf '=%.0s' {1..100}
Specifying a precision here truncates the string to fit the specified width (0
). As printf
reuses the format string to consume all of the arguments, this simply prints "="
100 times.
Using head
(printf
, etc) and tr
:
head -c 100 < /dev/zero | tr '' '='
printf %100s | tr " " "="
1
++ for thehead
/tr
solution, which works well even with high repeat counts (small caveat:head -c
is not POSIX-compliant, but both BSD and GNUhead
implement it); while the other two solutions will be slow in that case, they do have the advantage of working with multi-character strings, too.
– mklement0
Apr 29 '15 at 17:42
Usingyes
andhead
-- useful if you want a certain number of newlines:yes "" | head -n 100
.tr
can make it print any character:yes "" | head -n 100 | tr "n" "="; echo
– loxaxs
May 27 '18 at 9:09
Somewhat surprisingly:dd if=/dev/zero count=1 bs=100000000 | tr '' '=' >/dev/null
is significantly slower than thehead -c100000000 < /dev/zero | tr '' '=' >/dev/null
version. Of course you have to use a block size of 100M+ to measure the time difference reasonably. 100M bytes takes 1.7 s and 1 s with the two respective versions shown. I took off the tr and just dumped it to/dev/null
and got 0.287 s for thehead
version and 0.675 s for thedd
version for a billion bytes.
– Michael Goldshteyn
Aug 10 '18 at 23:10
For:dd if=/dev/zero count=1 bs=100000000 | tr '' '=' >/dev/null
=>0,21332 s, 469 MB/s
; For:dd if=/dev/zero count=100 bs=1000000| tr '' '=' >/dev/null
=>0,161579 s, 619 MB/s
;
– 3ED
Aug 18 '18 at 16:26
add a comment |
Tip of the hat to @gniourf_gniourf for his input.
Note: This answer does not answer the original question, but complements the existing, helpful answers by comparing performance.
Solutions are compared in terms of execution speed only - memory requirements are not taken 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 one instance of repeating characters, all existing solutions may be fine.
- With large repeat 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.
- In particular, avoid Bash's global substring replacement with large strings
The following are timings taken 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.
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
- Small repeat count: 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 small repetition counts in each iteration - avoid the multi-utility,
awk
, andperl
solutions.
- Large repeat count: 1000000 (1 million)
[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.
awk
refers to BSDawk
(as also found on OSX) - it's noticeably slower thangawk
(GNU Awk) and especiallymawk
.- Note that with large counts and multi-char. strings, memory consumption can become a consideration - the approaches differ in that respect.
Here's the Bash script (testrepeat
) that produced the above.
It takes 2 arguments:
- the character repeat count
- optionally, the number of test runs to perform and to calculate the average timing from
In other words: the timings above were obtained with testrepeat 100 1000
and 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 '' '=' >"$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 '' "=" >"$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 "%st", $1;
if ($2 ~ "^n/a") { print $2 } else { printf "%.4fn", $2 / count }}' |
column -s$'t' -t
It's interesting to see timing comparison, but I think in many programs output is buffered, so their timing can be altered if buffering was turned off.
– Sergiy Kolodyazhnyy
Jan 19 '17 at 4:25
add a comment |
I've just found a seriously easy way to do this using seq:
UPDATE: This works on the BSD seq
that comes with OS X. YMMV with other versions
seq -f "#" -s '' 10
Will print '#' 10 times, like this:
##########
-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
-f
and-s
seem to be important.
EDIT: Here it is in a handy function...
repeat () {
seq -f $1 -s '' $2; echo
}
Which you can call like this...
repeat "#" 10
NOTE: If you're repeating #
then the quotes are important!
7
This gives meseq: format ‘#’ has no % directive
.seq
is for numbers, not strings. See gnu.org/software/coreutils/manual/html_node/seq-invocation.html
– John B
Jul 7 '14 at 8:51
Ah, so I was using the BSD version of seq found on OS X. I'll update the answer. Which version are you using?
– Sam Salisbury
Jul 8 '14 at 9:20
I'm using seq from GNU coreutils.
– John B
Jul 8 '14 at 11:38
1
@JohnB: BSDseq
is being cleverly repurposed here to replicate strings: the format string passed to-f
- normally used to format the numbers being generated - contains only the string to replicate here so that the output contains copies of that string only. Unfortunately, GNUseq
insists on the presence of a number format in the format string, which is the error you're seeing.
– mklement0
Apr 29 '15 at 17:18
Nicely done; also works with multi-characters strings. Please use"$1"
(double quotes), so you can also pass in characters such as'*'
and strings with embedded whitespace. Finally, if you want to be able to use%
, you have to double it (otherwiseseq
will think it's part of a format specification such as%f
); using"${1//%/%%}"
would take care of that. Since (as you mention) you're using BSDseq
, this will work on BSD-like OSs in general (e.g., FreeBSD) - by contrast, it won't work on Linux, where GNUseq
is used.
– mklement0
Apr 29 '15 at 17:30
|
show 2 more comments
Here's two interesting ways:
ubuntu@ubuntu:~$ yes = | head -10 | paste -s -d '' -
==========
ubuntu@ubuntu:~$ yes = | head -10 | tr -d "n"
==========ubuntu@ubuntu:~$
Note these two are subtly different - The paste
method ends in a new line. The tr
method does not.
1
Nicely done; please note that BSDpaste
inexplicably requires-d ''
for specifying an empty delimiter, and fails with-d ''
--d ''
should work wit all POSIX-compatiblepaste
implementations and indeed works with GNUpaste
too.
– mklement0
Apr 29 '15 at 13:56
Similar in spirit, with fewer outboard tools:yes | mapfile -n 100 -C 'printf = #' -c 1
– bishop
Apr 27 '16 at 19:42
@bishop: While your command indeed creates one fewer subshell, it is still slower for higher repeat counts, and for lower repeat counts the difference probably doesn't matter; the exact threshold is probably both hardware- and OS-dependent, e.g., on my OSX 10.11.5 machine this answer is already faster at 500; trytime yes = | head -500 | paste -s -d '' -; time yes | mapfile -n 500 -C 'printf = #' -c 1
. More importantly, however: if you're usingprintf
anyway, you may as well go with the both simpler and more efficient approach from the accepted answer:printf '%.s=' $(seq 500)
– mklement0
Jul 17 '16 at 6:44
add a comment |
There is no simple way. Avoid loops using printf
and substitution.
str=$(printf "%40s")
echo ${str// /rep}
# echoes "rep" 40 times.
2
Nice, but only performs reasonably with small repeat counts. Here's a function wrapper that can be invoked asrepl = 100
, for instance (doesn't output a trailingn
):repl() { local ts=$(printf "%${2}s"); printf %s "${ts// /$1}"; }
– mklement0
Dec 7 '13 at 18:42
1
@mklement0 Nice of you to provide function versions of both solutions, +1 on both!
– Camilo Martin
Jan 2 '14 at 12:16
add a comment |
#!/usr/bin/awk -f
BEGIN {
OFS = "="
NF = 100
print
}
Or
#!/usr/bin/awk -f
BEGIN {
while (z++ < 100) printf "="
}
Example
3
Nicely done; this is POSIX-compliant and reasonably fast even with high repeat counts, while also supporting multi-character strings. Here's the shell version:awk 'BEGIN { while (c++ < 100) printf "=" }'
. Wrapped into a parameterized shell function (invoke asrepeat 100 =
, for instance):repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { txt=substr(txt, 2); while (i++ < count) printf txt }'; }
. (The dummy.
prefix char and complementarysubstr
call are needed to work around a bug in BSDawk
, where passing a variable value that starts with=
breaks the command.)
– mklement0
Apr 29 '15 at 18:12
1
TheNF = 100
solution is very clever (though to get 100=
, you must useNF = 101
). The caveats are that it crashes BSDawk
(but it's very fast withgawk
and even faster withmawk
), and that POSIX discusses neither assigning toNF
, nor use of fields inBEGIN
blocks. You can make it work in BSDawk
as well with a slight tweak:awk 'BEGIN { OFS = "="; $101=""; print }'
(but curiously, in BSDawk
that isn't faster than the loop solution). As a parameterized shell solution:repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { OFS=substr(txt, 2); $(count+1)=""; print }'; }
.
– mklement0
May 14 '15 at 19:01
Note to users - The NF=100 trick causes a segment fault on older awk. Theoriginal-awk
is the name under Linux of the older awk similar to BSD's awk, which has also been reported to crash, if you want to try this. Note that crashing is usually the first step toward finding an exploitable bug. This answer is so promoting insecure code.
– user2350426
Aug 25 '15 at 4:54
2
Note to users -original-awk
is non standard and not recommended
– Steven Penny
Aug 25 '15 at 23:02
An alternative to the first code snippet can beawk NF=100 OFS='=' <<< ""
(usingbash
andgawk
)
– oliv
May 24 '18 at 13:16
add a comment |
If you want POSIX-compliance and consistency across different implementations of echo
and printf
, and/or shells other than just 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)
...will produce the same output as perl -E 'say "=" x 100'
just about everywhere.
1
The problem is thatseq
is not a POSIX utility (though BSD and Linux systems have implementations of it) - you can do POSIX shell arithmetic with awhile
loop instead, as in @Xennex81's answer (withprintf "="
, as you correctly suggest, rather thanecho -n
).
– mklement0
Apr 29 '15 at 17:56
1
Oops, you're quite right. Things like that just slip past me sometimes as that standard makes no f'ing sense.cal
is POSIX.seq
is not. Anyway, rather than rewrite the answer with a while loop (as you say, that's already in other answers) I'll add a RYO function. More educational that way ;-).
– Geoff Nixon
May 3 '15 at 14:52
add a comment |
I guess the original purpose of the question was to do this just with the shell's built-in commands. So for
loops and printf
s would be legitimate, while rep
, perl
, and also jot
below would not. Still, the following command
jot -s "/" -b "\" $((COLUMNS/2))
for instance, prints a window-wide line of ////////////
2
Nicely done; this works well even with high repeat counts (while also supporting multi-character strings). To better illustrate the approach, here's the equivalent of the OP's command:jot -s '' -b '=' 100
. The caveat is that while BSD-like platforms, including OSX, come withjot
, Linux distros do not.
– mklement0
Apr 29 '15 at 17:49
1
Thanks, I like your use of -s '' even better. I've changed my scripts.
– Stefan Ludwig
Apr 29 '15 at 21:47
On recent Debian-based systems,apt install athena-jot
would providejot
.
– agc
Feb 8 at 20:54
add a comment |
As others have said, in bash brace expansion precedes parameter expansion, so {m,n}
ranges can only contain literals. seq
and jot
provide clean solutions but aren't fully portable from one system to another, even if you're using the same shell on each. (Though seq
is increasingly available; e.g., in FreeBSD 9.3 and higher.) eval
and other forms of indirection always work but are somewhat inelegant.
Fortunately, bash supports C-style for loops (with arithmetic expressions only). So here's a concise "pure bash" way:
repecho() { for ((i=0; i<$1; ++i)); do echo -n "$2"; done; echo; }
This takes the number of repetitions as the first argument and the string to be repeated (which may be a single character, as in the problem description) as the second argument. repecho 7 b
outputs bbbbbbb
(terminated by a newline).
Dennis Williamson gave essentially this solution four years ago in his excellent answer to Creating string of repeated characters in shell script. My function body differs slightly from the code there:
Since the focus here is on repeating a single character and the shell is bash, it's probably safe to use
echo
instead ofprintf
. And I read the problem description in this question as expressing a preference to print withecho
. The above function definition works in bash and ksh93. Althoughprintf
is more portable (and should usually be used for this sort of thing),echo
's syntax is arguably more readable.
Some shells'
echo
builtins interpret-
by itself as an option--even though the usual meaning of-
, to use stdin for input, is nonsensical forecho
. zsh does this. And there definitely existecho
s that don't recognize-n
, as it is not standard. (Many Bourne-style shells don't accept C-style for loops at all, thus theirecho
behavior needn't be considered..)
Here the task is to print the sequence; there, it was to assign it to a variable.
If $n
is the desired number of repetitions and you don't have to reuse it, and you want something even shorter:
while ((n--)); do echo -n "$s"; done; echo
n
must be a variable--this way doesn't work with positional parameters. $s
is the text to be repeated.
1
Strongly avoid doing loop versions.printf "%100s" | tr ' ' '='
is optimal.
– ocodo
Nov 4 '14 at 5:48
Good background info and kudos for packaging the functionality as a function, which works inzsh
as well, incidentally. The echo-in-a-loop approach works well for smaller repeat counts, but for larger ones there are POSIX-compliant alternatives based on utilities, as evidenced by @Slomojo's comment.
– mklement0
Apr 29 '15 at 15:53
Adding parentheses around your shorter loop preserves the value of n without affecting the echos:(while ((n--)); do echo -n "$s"; done; echo)
– user2350426
Aug 23 '15 at 4:24
add a comment |
A pure Bash way with no eval
, no subshells, no external tools, no brace expansions (i.e., you can have the number to repeat in a variable):
If you're given a variable n
that expands to a (non-negative) number and a variable pattern
, e.g.,
$ n=5
$ pattern=hello
$ printf -v output '%*s' "$n"
$ output=${output// /$pattern}
$ echo "$output"
hellohellohellohellohello
You can make a function with this:
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}"
}
With this set:
$ repeat 5 hello output
$ echo "$output"
hellohellohellohellohello
For this little trick we're using printf
quite a lot with:
-v varname
: instead of printing to standard output,printf
will put the content of the formatted string in variablevarname
.- '%*s':
printf
will use the argument to print the corresponding number of spaces. E.g.,printf '%*s' 42
will print 42 spaces. - Finally, when we have the wanted number of spaces in our variable, we use a parameter expansion to replace all the spaces by our pattern:
${var// /$pattern}
will expand to the expansion ofvar
with all the spaces replaced by the expansion of$pattern
.
You can also get rid of the tmp
variable in the repeat
function by using indirect expansion:
repeat() {
# $1=number of patterns to repeat
# $2=pattern
# $3=output variable name
printf -v "$3" '%*s' "$1"
printf -v "$3" '%s' "${!3// /$2}"
}
Interesting variation to pass the variable name in. While this solution is fine for repeat counts up to around 1,000 (and thus probably fine for most real-life applications, if I were to guess), it gets very slow for higher counts (see next comment).
– mklement0
Apr 29 '15 at 19:19
It seems thatbash
's global string replacement operations in the context of parameter expansion (${var//old/new}
) are particularly slow: excruciatingly slow in bash3.2.57
, and slow in bash4.3.30
, at least on my OSX 10.10.3 system on a 3.2 Ghz Intel Core i5 machine: With a count of 1,000, things are slow (3.2.57
) / fast (4.3.30
): 0.1 / 0.004 seconds. Increasing the count to 10,000 yields strikingly different numbers:repeat 10000 = var
takes around 80 seconds(!) in bash3.2.57
, and around 0.3 seconds in bash4.3.30
(much faster than on3.2.57
, but still slow).
– mklement0
Apr 29 '15 at 19:19
add a comment |
In bash 3.0 or higher
for i in {1..100};do echo -n =;done
add a comment |
for i in {1..100}
do
echo -n '='
done
echo
add a comment |
repeat() {
# $1=number of patterns to repeat
# $2=pattern
printf -v "TEMP" '%*s' "$1"
echo ${TEMP// /$2}
}
add a comment |
The question was about how to do it with echo
:
echo -e ''$_{1..100}'b='
This will will do exactly the same as perl -E 'say "=" x 100'
but with echo
only.
add a comment |
In case that you want to repeat a character n times being n a VARIABLE number of times depending on, say, the length of a string you can do:
#!/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)
It displays:
vari equals.............................: AB
Up to 10 positions I must fill with.....: 8 equal signs
AB========
length
won't work withexpr
, you probably meantn=$(expr 10 - ${#vari})
; however, it's simpler and more efficient to use Bash's arithmetic expansion:n=$(( 10 - ${#vari} ))
. Also, at the core of your answer is the very Perl approach that the OP is looking for a Bash alternative to.
– mklement0
Aug 7 '15 at 12:55
add a comment |
This is the longer version of what Eliah Kagan was espousing:
while [ $(( i-- )) -gt 0 ]; do echo -n " "; done
Of course you can use printf for that as well, but not really to my liking:
printf "%$(( i*2 ))s"
This version is Dash compatible:
until [ $(( i=i-1 )) -lt 0 ]; do echo -n " "; done
with i being the initial number.
In bash and with a positive n:while (( i-- )); do echo -n " "; done
works.
– user2350426
Aug 23 '15 at 4:27
add a comment |
Python is ubiquitous and works the same everywhere.
python -c "import sys; print('*' * int(sys.argv[1]))" "=" 100
Character and count are passed as separate parameters.
add a comment |
How could I do this with echo?
You can do this with echo
if the echo
is followed by sed
:
echo | sed -r ':a s/^(.*)$/=1/; /^={100}$/q; ba'
Actually, that echo
is unnecessary there.
add a comment |
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
}
Sample runs
$ repeatString 'a1' 10
a1a1a1a1a1a1a1a1a1a1
$ repeatString 'a1' 0
$ repeatString '' 10
Reference lib at: https://github.com/gdbtek/linux-cookbooks/blob/master/libraries/util.bash
add a comment |
Simplest is to use this one-liner in csh/tcsh:
printf "%50sn" '' | tr '[:blank:]' '[=]'
add a comment |
My answer is a bit more complicated, and probably not perfect, but for those looking to output large numbers, I was able to do around 10 million in 3 seconds.
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}
}
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f5349718%2fhow-can-i-repeat-a-character-in-bash%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
23 Answers
23
active
oldest
votes
23 Answers
23
active
oldest
votes
active
oldest
votes
active
oldest
votes
You can use:
printf '=%.0s' {1..100}
How this works:
Bash expands {1..100} so the command becomes:
printf '=%.0s' 1 2 3 4 ... 100
I've set printf's format to =%.0s
which means that it will always print a single =
no matter what argument it is given. Therefore it prints 100 =
s.
11
Great solution that performs reasonably well even with large repeat counts. Here's a function wrapper you can invoke withrepl = 100
, for instance (eval
trickery is required, unfortunately, for basing the brace expansion on a variable):repl() { printf "$1"'%.s' $(eval "echo {1.."$(($2))"}"); }
– mklement0
Dec 7 '13 at 21:34
4
Is it possible to set the upper limit using a var? I've tried and can't get it to work.
– Mike Purcell
Jan 10 '14 at 20:30
54
You can't use variables within brace expansion. Useseq
instead e.g.$(seq 1 $limit)
.
– dogbane
Jan 11 '14 at 8:22
10
If you functionalise this it's best to rearrange it from$s%.0s
to%.0s$s
otherwise dashes cause aprintf
error.
– KomodoDave
Jul 30 '14 at 7:35
2
This made me notice a behaviour of Bash'sprintf
: it continues to apply the format string until there are no arguments left. I had assumed it processed the format string only once!
– Jeenu
Jan 8 '15 at 10:25
|
show 11 more comments
You can use:
printf '=%.0s' {1..100}
How this works:
Bash expands {1..100} so the command becomes:
printf '=%.0s' 1 2 3 4 ... 100
I've set printf's format to =%.0s
which means that it will always print a single =
no matter what argument it is given. Therefore it prints 100 =
s.
11
Great solution that performs reasonably well even with large repeat counts. Here's a function wrapper you can invoke withrepl = 100
, for instance (eval
trickery is required, unfortunately, for basing the brace expansion on a variable):repl() { printf "$1"'%.s' $(eval "echo {1.."$(($2))"}"); }
– mklement0
Dec 7 '13 at 21:34
4
Is it possible to set the upper limit using a var? I've tried and can't get it to work.
– Mike Purcell
Jan 10 '14 at 20:30
54
You can't use variables within brace expansion. Useseq
instead e.g.$(seq 1 $limit)
.
– dogbane
Jan 11 '14 at 8:22
10
If you functionalise this it's best to rearrange it from$s%.0s
to%.0s$s
otherwise dashes cause aprintf
error.
– KomodoDave
Jul 30 '14 at 7:35
2
This made me notice a behaviour of Bash'sprintf
: it continues to apply the format string until there are no arguments left. I had assumed it processed the format string only once!
– Jeenu
Jan 8 '15 at 10:25
|
show 11 more comments
You can use:
printf '=%.0s' {1..100}
How this works:
Bash expands {1..100} so the command becomes:
printf '=%.0s' 1 2 3 4 ... 100
I've set printf's format to =%.0s
which means that it will always print a single =
no matter what argument it is given. Therefore it prints 100 =
s.
You can use:
printf '=%.0s' {1..100}
How this works:
Bash expands {1..100} so the command becomes:
printf '=%.0s' 1 2 3 4 ... 100
I've set printf's format to =%.0s
which means that it will always print a single =
no matter what argument it is given. Therefore it prints 100 =
s.
edited Mar 18 '11 at 9:28
answered Mar 18 '11 at 8:58


dogbanedogbane
192k65320374
192k65320374
11
Great solution that performs reasonably well even with large repeat counts. Here's a function wrapper you can invoke withrepl = 100
, for instance (eval
trickery is required, unfortunately, for basing the brace expansion on a variable):repl() { printf "$1"'%.s' $(eval "echo {1.."$(($2))"}"); }
– mklement0
Dec 7 '13 at 21:34
4
Is it possible to set the upper limit using a var? I've tried and can't get it to work.
– Mike Purcell
Jan 10 '14 at 20:30
54
You can't use variables within brace expansion. Useseq
instead e.g.$(seq 1 $limit)
.
– dogbane
Jan 11 '14 at 8:22
10
If you functionalise this it's best to rearrange it from$s%.0s
to%.0s$s
otherwise dashes cause aprintf
error.
– KomodoDave
Jul 30 '14 at 7:35
2
This made me notice a behaviour of Bash'sprintf
: it continues to apply the format string until there are no arguments left. I had assumed it processed the format string only once!
– Jeenu
Jan 8 '15 at 10:25
|
show 11 more comments
11
Great solution that performs reasonably well even with large repeat counts. Here's a function wrapper you can invoke withrepl = 100
, for instance (eval
trickery is required, unfortunately, for basing the brace expansion on a variable):repl() { printf "$1"'%.s' $(eval "echo {1.."$(($2))"}"); }
– mklement0
Dec 7 '13 at 21:34
4
Is it possible to set the upper limit using a var? I've tried and can't get it to work.
– Mike Purcell
Jan 10 '14 at 20:30
54
You can't use variables within brace expansion. Useseq
instead e.g.$(seq 1 $limit)
.
– dogbane
Jan 11 '14 at 8:22
10
If you functionalise this it's best to rearrange it from$s%.0s
to%.0s$s
otherwise dashes cause aprintf
error.
– KomodoDave
Jul 30 '14 at 7:35
2
This made me notice a behaviour of Bash'sprintf
: it continues to apply the format string until there are no arguments left. I had assumed it processed the format string only once!
– Jeenu
Jan 8 '15 at 10:25
11
11
Great solution that performs reasonably well even with large repeat counts. Here's a function wrapper you can invoke with
repl = 100
, for instance (eval
trickery is required, unfortunately, for basing the brace expansion on a variable): repl() { printf "$1"'%.s' $(eval "echo {1.."$(($2))"}"); }
– mklement0
Dec 7 '13 at 21:34
Great solution that performs reasonably well even with large repeat counts. Here's a function wrapper you can invoke with
repl = 100
, for instance (eval
trickery is required, unfortunately, for basing the brace expansion on a variable): repl() { printf "$1"'%.s' $(eval "echo {1.."$(($2))"}"); }
– mklement0
Dec 7 '13 at 21:34
4
4
Is it possible to set the upper limit using a var? I've tried and can't get it to work.
– Mike Purcell
Jan 10 '14 at 20:30
Is it possible to set the upper limit using a var? I've tried and can't get it to work.
– Mike Purcell
Jan 10 '14 at 20:30
54
54
You can't use variables within brace expansion. Use
seq
instead e.g. $(seq 1 $limit)
.– dogbane
Jan 11 '14 at 8:22
You can't use variables within brace expansion. Use
seq
instead e.g. $(seq 1 $limit)
.– dogbane
Jan 11 '14 at 8:22
10
10
If you functionalise this it's best to rearrange it from
$s%.0s
to %.0s$s
otherwise dashes cause a printf
error.– KomodoDave
Jul 30 '14 at 7:35
If you functionalise this it's best to rearrange it from
$s%.0s
to %.0s$s
otherwise dashes cause a printf
error.– KomodoDave
Jul 30 '14 at 7:35
2
2
This made me notice a behaviour of Bash's
printf
: it continues to apply the format string until there are no arguments left. I had assumed it processed the format string only once!– Jeenu
Jan 8 '15 at 10:25
This made me notice a behaviour of Bash's
printf
: it continues to apply the format string until there are no arguments left. I had assumed it processed the format string only once!– Jeenu
Jan 8 '15 at 10:25
|
show 11 more comments
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:)
2
Note that the first option with seq prints one less than the number given, so that example will print 99=
characters.
– Camilo Martin
Jan 2 '14 at 16:10
11
printf
tr
is the only POSIX solution becauseseq
,yes
and{1..3}
are not POSIX.
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Apr 10 '14 at 11:02
2
To repeat a string rather than just a single character:printf %100s | sed 's/ /abc/g'
- outputs 'abcabcabc...'
– John Rix
Sep 11 '14 at 12:51
1
+1 for using no loops and only one external command (tr
). You could also extend it to something likeprintf "%${COLUMNS}sn" | tr " " "="
.
– musiphil
Mar 16 '15 at 20:59
2
@mklement0 Well, I was hoping you were counting the last newline by mistake withwc
. The only conclusion I can take from this is "seq
shouldn't be used".
– Camilo Martin
May 3 '15 at 20:17
|
show 5 more comments
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:)
2
Note that the first option with seq prints one less than the number given, so that example will print 99=
characters.
– Camilo Martin
Jan 2 '14 at 16:10
11
printf
tr
is the only POSIX solution becauseseq
,yes
and{1..3}
are not POSIX.
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Apr 10 '14 at 11:02
2
To repeat a string rather than just a single character:printf %100s | sed 's/ /abc/g'
- outputs 'abcabcabc...'
– John Rix
Sep 11 '14 at 12:51
1
+1 for using no loops and only one external command (tr
). You could also extend it to something likeprintf "%${COLUMNS}sn" | tr " " "="
.
– musiphil
Mar 16 '15 at 20:59
2
@mklement0 Well, I was hoping you were counting the last newline by mistake withwc
. The only conclusion I can take from this is "seq
shouldn't be used".
– Camilo Martin
May 3 '15 at 20:17
|
show 5 more comments
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:)
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:)
edited Mar 18 '11 at 9:55
answered Mar 18 '11 at 8:52
user332325
2
Note that the first option with seq prints one less than the number given, so that example will print 99=
characters.
– Camilo Martin
Jan 2 '14 at 16:10
11
printf
tr
is the only POSIX solution becauseseq
,yes
and{1..3}
are not POSIX.
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Apr 10 '14 at 11:02
2
To repeat a string rather than just a single character:printf %100s | sed 's/ /abc/g'
- outputs 'abcabcabc...'
– John Rix
Sep 11 '14 at 12:51
1
+1 for using no loops and only one external command (tr
). You could also extend it to something likeprintf "%${COLUMNS}sn" | tr " " "="
.
– musiphil
Mar 16 '15 at 20:59
2
@mklement0 Well, I was hoping you were counting the last newline by mistake withwc
. The only conclusion I can take from this is "seq
shouldn't be used".
– Camilo Martin
May 3 '15 at 20:17
|
show 5 more comments
2
Note that the first option with seq prints one less than the number given, so that example will print 99=
characters.
– Camilo Martin
Jan 2 '14 at 16:10
11
printf
tr
is the only POSIX solution becauseseq
,yes
and{1..3}
are not POSIX.
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Apr 10 '14 at 11:02
2
To repeat a string rather than just a single character:printf %100s | sed 's/ /abc/g'
- outputs 'abcabcabc...'
– John Rix
Sep 11 '14 at 12:51
1
+1 for using no loops and only one external command (tr
). You could also extend it to something likeprintf "%${COLUMNS}sn" | tr " " "="
.
– musiphil
Mar 16 '15 at 20:59
2
@mklement0 Well, I was hoping you were counting the last newline by mistake withwc
. The only conclusion I can take from this is "seq
shouldn't be used".
– Camilo Martin
May 3 '15 at 20:17
2
2
Note that the first option with seq prints one less than the number given, so that example will print 99
=
characters.– Camilo Martin
Jan 2 '14 at 16:10
Note that the first option with seq prints one less than the number given, so that example will print 99
=
characters.– Camilo Martin
Jan 2 '14 at 16:10
11
11
printf
tr
is the only POSIX solution because seq
, yes
and {1..3}
are not POSIX.– Ciro Santilli 新疆改造中心 六四事件 法轮功
Apr 10 '14 at 11:02
printf
tr
is the only POSIX solution because seq
, yes
and {1..3}
are not POSIX.– Ciro Santilli 新疆改造中心 六四事件 法轮功
Apr 10 '14 at 11:02
2
2
To repeat a string rather than just a single character:
printf %100s | sed 's/ /abc/g'
- outputs 'abcabcabc...'– John Rix
Sep 11 '14 at 12:51
To repeat a string rather than just a single character:
printf %100s | sed 's/ /abc/g'
- outputs 'abcabcabc...'– John Rix
Sep 11 '14 at 12:51
1
1
+1 for using no loops and only one external command (
tr
). You could also extend it to something like printf "%${COLUMNS}sn" | tr " " "="
.– musiphil
Mar 16 '15 at 20:59
+1 for using no loops and only one external command (
tr
). You could also extend it to something like printf "%${COLUMNS}sn" | tr " " "="
.– musiphil
Mar 16 '15 at 20:59
2
2
@mklement0 Well, I was hoping you were counting the last newline by mistake with
wc
. The only conclusion I can take from this is "seq
shouldn't be used".– Camilo Martin
May 3 '15 at 20:17
@mklement0 Well, I was hoping you were counting the last newline by mistake with
wc
. The only conclusion I can take from this is "seq
shouldn't be used".– Camilo Martin
May 3 '15 at 20:17
|
show 5 more comments
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
Using the printf
builtin:
printf '=%.0s' {1..100}
Specifying a precision here truncates the string to fit the specified width (0
). As printf
reuses the format string to consume all of the arguments, this simply prints "="
100 times.
Using head
(printf
, etc) and tr
:
head -c 100 < /dev/zero | tr '' '='
printf %100s | tr " " "="
1
++ for thehead
/tr
solution, which works well even with high repeat counts (small caveat:head -c
is not POSIX-compliant, but both BSD and GNUhead
implement it); while the other two solutions will be slow in that case, they do have the advantage of working with multi-character strings, too.
– mklement0
Apr 29 '15 at 17:42
Usingyes
andhead
-- useful if you want a certain number of newlines:yes "" | head -n 100
.tr
can make it print any character:yes "" | head -n 100 | tr "n" "="; echo
– loxaxs
May 27 '18 at 9:09
Somewhat surprisingly:dd if=/dev/zero count=1 bs=100000000 | tr '' '=' >/dev/null
is significantly slower than thehead -c100000000 < /dev/zero | tr '' '=' >/dev/null
version. Of course you have to use a block size of 100M+ to measure the time difference reasonably. 100M bytes takes 1.7 s and 1 s with the two respective versions shown. I took off the tr and just dumped it to/dev/null
and got 0.287 s for thehead
version and 0.675 s for thedd
version for a billion bytes.
– Michael Goldshteyn
Aug 10 '18 at 23:10
For:dd if=/dev/zero count=1 bs=100000000 | tr '' '=' >/dev/null
=>0,21332 s, 469 MB/s
; For:dd if=/dev/zero count=100 bs=1000000| tr '' '=' >/dev/null
=>0,161579 s, 619 MB/s
;
– 3ED
Aug 18 '18 at 16:26
add a comment |
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
Using the printf
builtin:
printf '=%.0s' {1..100}
Specifying a precision here truncates the string to fit the specified width (0
). As printf
reuses the format string to consume all of the arguments, this simply prints "="
100 times.
Using head
(printf
, etc) and tr
:
head -c 100 < /dev/zero | tr '' '='
printf %100s | tr " " "="
1
++ for thehead
/tr
solution, which works well even with high repeat counts (small caveat:head -c
is not POSIX-compliant, but both BSD and GNUhead
implement it); while the other two solutions will be slow in that case, they do have the advantage of working with multi-character strings, too.
– mklement0
Apr 29 '15 at 17:42
Usingyes
andhead
-- useful if you want a certain number of newlines:yes "" | head -n 100
.tr
can make it print any character:yes "" | head -n 100 | tr "n" "="; echo
– loxaxs
May 27 '18 at 9:09
Somewhat surprisingly:dd if=/dev/zero count=1 bs=100000000 | tr '' '=' >/dev/null
is significantly slower than thehead -c100000000 < /dev/zero | tr '' '=' >/dev/null
version. Of course you have to use a block size of 100M+ to measure the time difference reasonably. 100M bytes takes 1.7 s and 1 s with the two respective versions shown. I took off the tr and just dumped it to/dev/null
and got 0.287 s for thehead
version and 0.675 s for thedd
version for a billion bytes.
– Michael Goldshteyn
Aug 10 '18 at 23:10
For:dd if=/dev/zero count=1 bs=100000000 | tr '' '=' >/dev/null
=>0,21332 s, 469 MB/s
; For:dd if=/dev/zero count=100 bs=1000000| tr '' '=' >/dev/null
=>0,161579 s, 619 MB/s
;
– 3ED
Aug 18 '18 at 16:26
add a comment |
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
Using the printf
builtin:
printf '=%.0s' {1..100}
Specifying a precision here truncates the string to fit the specified width (0
). As printf
reuses the format string to consume all of the arguments, this simply prints "="
100 times.
Using head
(printf
, etc) and tr
:
head -c 100 < /dev/zero | tr '' '='
printf %100s | tr " " "="
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
Using the printf
builtin:
printf '=%.0s' {1..100}
Specifying a precision here truncates the string to fit the specified width (0
). As printf
reuses the format string to consume all of the arguments, this simply prints "="
100 times.
Using head
(printf
, etc) and tr
:
head -c 100 < /dev/zero | tr '' '='
printf %100s | tr " " "="
edited Aug 25 '16 at 11:02
answered Mar 18 '11 at 8:50
Eugene YarmashEugene Yarmash
85.2k23184265
85.2k23184265
1
++ for thehead
/tr
solution, which works well even with high repeat counts (small caveat:head -c
is not POSIX-compliant, but both BSD and GNUhead
implement it); while the other two solutions will be slow in that case, they do have the advantage of working with multi-character strings, too.
– mklement0
Apr 29 '15 at 17:42
Usingyes
andhead
-- useful if you want a certain number of newlines:yes "" | head -n 100
.tr
can make it print any character:yes "" | head -n 100 | tr "n" "="; echo
– loxaxs
May 27 '18 at 9:09
Somewhat surprisingly:dd if=/dev/zero count=1 bs=100000000 | tr '' '=' >/dev/null
is significantly slower than thehead -c100000000 < /dev/zero | tr '' '=' >/dev/null
version. Of course you have to use a block size of 100M+ to measure the time difference reasonably. 100M bytes takes 1.7 s and 1 s with the two respective versions shown. I took off the tr and just dumped it to/dev/null
and got 0.287 s for thehead
version and 0.675 s for thedd
version for a billion bytes.
– Michael Goldshteyn
Aug 10 '18 at 23:10
For:dd if=/dev/zero count=1 bs=100000000 | tr '' '=' >/dev/null
=>0,21332 s, 469 MB/s
; For:dd if=/dev/zero count=100 bs=1000000| tr '' '=' >/dev/null
=>0,161579 s, 619 MB/s
;
– 3ED
Aug 18 '18 at 16:26
add a comment |
1
++ for thehead
/tr
solution, which works well even with high repeat counts (small caveat:head -c
is not POSIX-compliant, but both BSD and GNUhead
implement it); while the other two solutions will be slow in that case, they do have the advantage of working with multi-character strings, too.
– mklement0
Apr 29 '15 at 17:42
Usingyes
andhead
-- useful if you want a certain number of newlines:yes "" | head -n 100
.tr
can make it print any character:yes "" | head -n 100 | tr "n" "="; echo
– loxaxs
May 27 '18 at 9:09
Somewhat surprisingly:dd if=/dev/zero count=1 bs=100000000 | tr '' '=' >/dev/null
is significantly slower than thehead -c100000000 < /dev/zero | tr '' '=' >/dev/null
version. Of course you have to use a block size of 100M+ to measure the time difference reasonably. 100M bytes takes 1.7 s and 1 s with the two respective versions shown. I took off the tr and just dumped it to/dev/null
and got 0.287 s for thehead
version and 0.675 s for thedd
version for a billion bytes.
– Michael Goldshteyn
Aug 10 '18 at 23:10
For:dd if=/dev/zero count=1 bs=100000000 | tr '' '=' >/dev/null
=>0,21332 s, 469 MB/s
; For:dd if=/dev/zero count=100 bs=1000000| tr '' '=' >/dev/null
=>0,161579 s, 619 MB/s
;
– 3ED
Aug 18 '18 at 16:26
1
1
++ for the
head
/ tr
solution, which works well even with high repeat counts (small caveat: head -c
is not POSIX-compliant, but both BSD and GNU head
implement it); while the other two solutions will be slow in that case, they do have the advantage of working with multi-character strings, too.– mklement0
Apr 29 '15 at 17:42
++ for the
head
/ tr
solution, which works well even with high repeat counts (small caveat: head -c
is not POSIX-compliant, but both BSD and GNU head
implement it); while the other two solutions will be slow in that case, they do have the advantage of working with multi-character strings, too.– mklement0
Apr 29 '15 at 17:42
Using
yes
and head
-- useful if you want a certain number of newlines: yes "" | head -n 100
. tr
can make it print any character: yes "" | head -n 100 | tr "n" "="; echo
– loxaxs
May 27 '18 at 9:09
Using
yes
and head
-- useful if you want a certain number of newlines: yes "" | head -n 100
. tr
can make it print any character: yes "" | head -n 100 | tr "n" "="; echo
– loxaxs
May 27 '18 at 9:09
Somewhat surprisingly:
dd if=/dev/zero count=1 bs=100000000 | tr '' '=' >/dev/null
is significantly slower than the head -c100000000 < /dev/zero | tr '' '=' >/dev/null
version. Of course you have to use a block size of 100M+ to measure the time difference reasonably. 100M bytes takes 1.7 s and 1 s with the two respective versions shown. I took off the tr and just dumped it to /dev/null
and got 0.287 s for the head
version and 0.675 s for the dd
version for a billion bytes.– Michael Goldshteyn
Aug 10 '18 at 23:10
Somewhat surprisingly:
dd if=/dev/zero count=1 bs=100000000 | tr '' '=' >/dev/null
is significantly slower than the head -c100000000 < /dev/zero | tr '' '=' >/dev/null
version. Of course you have to use a block size of 100M+ to measure the time difference reasonably. 100M bytes takes 1.7 s and 1 s with the two respective versions shown. I took off the tr and just dumped it to /dev/null
and got 0.287 s for the head
version and 0.675 s for the dd
version for a billion bytes.– Michael Goldshteyn
Aug 10 '18 at 23:10
For:
dd if=/dev/zero count=1 bs=100000000 | tr '' '=' >/dev/null
=> 0,21332 s, 469 MB/s
; For: dd if=/dev/zero count=100 bs=1000000| tr '' '=' >/dev/null
=> 0,161579 s, 619 MB/s
;– 3ED
Aug 18 '18 at 16:26
For:
dd if=/dev/zero count=1 bs=100000000 | tr '' '=' >/dev/null
=> 0,21332 s, 469 MB/s
; For: dd if=/dev/zero count=100 bs=1000000| tr '' '=' >/dev/null
=> 0,161579 s, 619 MB/s
;– 3ED
Aug 18 '18 at 16:26
add a comment |
Tip of the hat to @gniourf_gniourf for his input.
Note: This answer does not answer the original question, but complements the existing, helpful answers by comparing performance.
Solutions are compared in terms of execution speed only - memory requirements are not taken 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 one instance of repeating characters, all existing solutions may be fine.
- With large repeat 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.
- In particular, avoid Bash's global substring replacement with large strings
The following are timings taken 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.
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
- Small repeat count: 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 small repetition counts in each iteration - avoid the multi-utility,
awk
, andperl
solutions.
- Large repeat count: 1000000 (1 million)
[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.
awk
refers to BSDawk
(as also found on OSX) - it's noticeably slower thangawk
(GNU Awk) and especiallymawk
.- Note that with large counts and multi-char. strings, memory consumption can become a consideration - the approaches differ in that respect.
Here's the Bash script (testrepeat
) that produced the above.
It takes 2 arguments:
- the character repeat count
- optionally, the number of test runs to perform and to calculate the average timing from
In other words: the timings above were obtained with testrepeat 100 1000
and 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 '' '=' >"$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 '' "=" >"$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 "%st", $1;
if ($2 ~ "^n/a") { print $2 } else { printf "%.4fn", $2 / count }}' |
column -s$'t' -t
It's interesting to see timing comparison, but I think in many programs output is buffered, so their timing can be altered if buffering was turned off.
– Sergiy Kolodyazhnyy
Jan 19 '17 at 4:25
add a comment |
Tip of the hat to @gniourf_gniourf for his input.
Note: This answer does not answer the original question, but complements the existing, helpful answers by comparing performance.
Solutions are compared in terms of execution speed only - memory requirements are not taken 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 one instance of repeating characters, all existing solutions may be fine.
- With large repeat 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.
- In particular, avoid Bash's global substring replacement with large strings
The following are timings taken 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.
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
- Small repeat count: 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 small repetition counts in each iteration - avoid the multi-utility,
awk
, andperl
solutions.
- Large repeat count: 1000000 (1 million)
[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.
awk
refers to BSDawk
(as also found on OSX) - it's noticeably slower thangawk
(GNU Awk) and especiallymawk
.- Note that with large counts and multi-char. strings, memory consumption can become a consideration - the approaches differ in that respect.
Here's the Bash script (testrepeat
) that produced the above.
It takes 2 arguments:
- the character repeat count
- optionally, the number of test runs to perform and to calculate the average timing from
In other words: the timings above were obtained with testrepeat 100 1000
and 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 '' '=' >"$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 '' "=" >"$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 "%st", $1;
if ($2 ~ "^n/a") { print $2 } else { printf "%.4fn", $2 / count }}' |
column -s$'t' -t
It's interesting to see timing comparison, but I think in many programs output is buffered, so their timing can be altered if buffering was turned off.
– Sergiy Kolodyazhnyy
Jan 19 '17 at 4:25
add a comment |
Tip of the hat to @gniourf_gniourf for his input.
Note: This answer does not answer the original question, but complements the existing, helpful answers by comparing performance.
Solutions are compared in terms of execution speed only - memory requirements are not taken 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 one instance of repeating characters, all existing solutions may be fine.
- With large repeat 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.
- In particular, avoid Bash's global substring replacement with large strings
The following are timings taken 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.
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
- Small repeat count: 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 small repetition counts in each iteration - avoid the multi-utility,
awk
, andperl
solutions.
- Large repeat count: 1000000 (1 million)
[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.
awk
refers to BSDawk
(as also found on OSX) - it's noticeably slower thangawk
(GNU Awk) and especiallymawk
.- Note that with large counts and multi-char. strings, memory consumption can become a consideration - the approaches differ in that respect.
Here's the Bash script (testrepeat
) that produced the above.
It takes 2 arguments:
- the character repeat count
- optionally, the number of test runs to perform and to calculate the average timing from
In other words: the timings above were obtained with testrepeat 100 1000
and 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 '' '=' >"$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 '' "=" >"$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 "%st", $1;
if ($2 ~ "^n/a") { print $2 } else { printf "%.4fn", $2 / count }}' |
column -s$'t' -t
Tip of the hat to @gniourf_gniourf for his input.
Note: This answer does not answer the original question, but complements the existing, helpful answers by comparing performance.
Solutions are compared in terms of execution speed only - memory requirements are not taken 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 one instance of repeating characters, all existing solutions may be fine.
- With large repeat 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.
- In particular, avoid Bash's global substring replacement with large strings
The following are timings taken 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.
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
- Small repeat count: 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 small repetition counts in each iteration - avoid the multi-utility,
awk
, andperl
solutions.
- Large repeat count: 1000000 (1 million)
[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.
awk
refers to BSDawk
(as also found on OSX) - it's noticeably slower thangawk
(GNU Awk) and especiallymawk
.- Note that with large counts and multi-char. strings, memory consumption can become a consideration - the approaches differ in that respect.
Here's the Bash script (testrepeat
) that produced the above.
It takes 2 arguments:
- the character repeat count
- optionally, the number of test runs to perform and to calculate the average timing from
In other words: the timings above were obtained with testrepeat 100 1000
and 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 '' '=' >"$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 '' "=" >"$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 "%st", $1;
if ($2 ~ "^n/a") { print $2 } else { printf "%.4fn", $2 / count }}' |
column -s$'t' -t
edited May 23 '17 at 11:47
Community♦
11
11
answered May 17 '15 at 15:04
mklement0mklement0
133k21249287
133k21249287
It's interesting to see timing comparison, but I think in many programs output is buffered, so their timing can be altered if buffering was turned off.
– Sergiy Kolodyazhnyy
Jan 19 '17 at 4:25
add a comment |
It's interesting to see timing comparison, but I think in many programs output is buffered, so their timing can be altered if buffering was turned off.
– Sergiy Kolodyazhnyy
Jan 19 '17 at 4:25
It's interesting to see timing comparison, but I think in many programs output is buffered, so their timing can be altered if buffering was turned off.
– Sergiy Kolodyazhnyy
Jan 19 '17 at 4:25
It's interesting to see timing comparison, but I think in many programs output is buffered, so their timing can be altered if buffering was turned off.
– Sergiy Kolodyazhnyy
Jan 19 '17 at 4:25
add a comment |
I've just found a seriously easy way to do this using seq:
UPDATE: This works on the BSD seq
that comes with OS X. YMMV with other versions
seq -f "#" -s '' 10
Will print '#' 10 times, like this:
##########
-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
-f
and-s
seem to be important.
EDIT: Here it is in a handy function...
repeat () {
seq -f $1 -s '' $2; echo
}
Which you can call like this...
repeat "#" 10
NOTE: If you're repeating #
then the quotes are important!
7
This gives meseq: format ‘#’ has no % directive
.seq
is for numbers, not strings. See gnu.org/software/coreutils/manual/html_node/seq-invocation.html
– John B
Jul 7 '14 at 8:51
Ah, so I was using the BSD version of seq found on OS X. I'll update the answer. Which version are you using?
– Sam Salisbury
Jul 8 '14 at 9:20
I'm using seq from GNU coreutils.
– John B
Jul 8 '14 at 11:38
1
@JohnB: BSDseq
is being cleverly repurposed here to replicate strings: the format string passed to-f
- normally used to format the numbers being generated - contains only the string to replicate here so that the output contains copies of that string only. Unfortunately, GNUseq
insists on the presence of a number format in the format string, which is the error you're seeing.
– mklement0
Apr 29 '15 at 17:18
Nicely done; also works with multi-characters strings. Please use"$1"
(double quotes), so you can also pass in characters such as'*'
and strings with embedded whitespace. Finally, if you want to be able to use%
, you have to double it (otherwiseseq
will think it's part of a format specification such as%f
); using"${1//%/%%}"
would take care of that. Since (as you mention) you're using BSDseq
, this will work on BSD-like OSs in general (e.g., FreeBSD) - by contrast, it won't work on Linux, where GNUseq
is used.
– mklement0
Apr 29 '15 at 17:30
|
show 2 more comments
I've just found a seriously easy way to do this using seq:
UPDATE: This works on the BSD seq
that comes with OS X. YMMV with other versions
seq -f "#" -s '' 10
Will print '#' 10 times, like this:
##########
-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
-f
and-s
seem to be important.
EDIT: Here it is in a handy function...
repeat () {
seq -f $1 -s '' $2; echo
}
Which you can call like this...
repeat "#" 10
NOTE: If you're repeating #
then the quotes are important!
7
This gives meseq: format ‘#’ has no % directive
.seq
is for numbers, not strings. See gnu.org/software/coreutils/manual/html_node/seq-invocation.html
– John B
Jul 7 '14 at 8:51
Ah, so I was using the BSD version of seq found on OS X. I'll update the answer. Which version are you using?
– Sam Salisbury
Jul 8 '14 at 9:20
I'm using seq from GNU coreutils.
– John B
Jul 8 '14 at 11:38
1
@JohnB: BSDseq
is being cleverly repurposed here to replicate strings: the format string passed to-f
- normally used to format the numbers being generated - contains only the string to replicate here so that the output contains copies of that string only. Unfortunately, GNUseq
insists on the presence of a number format in the format string, which is the error you're seeing.
– mklement0
Apr 29 '15 at 17:18
Nicely done; also works with multi-characters strings. Please use"$1"
(double quotes), so you can also pass in characters such as'*'
and strings with embedded whitespace. Finally, if you want to be able to use%
, you have to double it (otherwiseseq
will think it's part of a format specification such as%f
); using"${1//%/%%}"
would take care of that. Since (as you mention) you're using BSDseq
, this will work on BSD-like OSs in general (e.g., FreeBSD) - by contrast, it won't work on Linux, where GNUseq
is used.
– mklement0
Apr 29 '15 at 17:30
|
show 2 more comments
I've just found a seriously easy way to do this using seq:
UPDATE: This works on the BSD seq
that comes with OS X. YMMV with other versions
seq -f "#" -s '' 10
Will print '#' 10 times, like this:
##########
-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
-f
and-s
seem to be important.
EDIT: Here it is in a handy function...
repeat () {
seq -f $1 -s '' $2; echo
}
Which you can call like this...
repeat "#" 10
NOTE: If you're repeating #
then the quotes are important!
I've just found a seriously easy way to do this using seq:
UPDATE: This works on the BSD seq
that comes with OS X. YMMV with other versions
seq -f "#" -s '' 10
Will print '#' 10 times, like this:
##########
-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
-f
and-s
seem to be important.
EDIT: Here it is in a handy function...
repeat () {
seq -f $1 -s '' $2; echo
}
Which you can call like this...
repeat "#" 10
NOTE: If you're repeating #
then the quotes are important!
edited Apr 30 '15 at 0:54
Steven Penny
1
1
answered May 2 '14 at 11:04
Sam SalisburySam Salisbury
748715
748715
7
This gives meseq: format ‘#’ has no % directive
.seq
is for numbers, not strings. See gnu.org/software/coreutils/manual/html_node/seq-invocation.html
– John B
Jul 7 '14 at 8:51
Ah, so I was using the BSD version of seq found on OS X. I'll update the answer. Which version are you using?
– Sam Salisbury
Jul 8 '14 at 9:20
I'm using seq from GNU coreutils.
– John B
Jul 8 '14 at 11:38
1
@JohnB: BSDseq
is being cleverly repurposed here to replicate strings: the format string passed to-f
- normally used to format the numbers being generated - contains only the string to replicate here so that the output contains copies of that string only. Unfortunately, GNUseq
insists on the presence of a number format in the format string, which is the error you're seeing.
– mklement0
Apr 29 '15 at 17:18
Nicely done; also works with multi-characters strings. Please use"$1"
(double quotes), so you can also pass in characters such as'*'
and strings with embedded whitespace. Finally, if you want to be able to use%
, you have to double it (otherwiseseq
will think it's part of a format specification such as%f
); using"${1//%/%%}"
would take care of that. Since (as you mention) you're using BSDseq
, this will work on BSD-like OSs in general (e.g., FreeBSD) - by contrast, it won't work on Linux, where GNUseq
is used.
– mklement0
Apr 29 '15 at 17:30
|
show 2 more comments
7
This gives meseq: format ‘#’ has no % directive
.seq
is for numbers, not strings. See gnu.org/software/coreutils/manual/html_node/seq-invocation.html
– John B
Jul 7 '14 at 8:51
Ah, so I was using the BSD version of seq found on OS X. I'll update the answer. Which version are you using?
– Sam Salisbury
Jul 8 '14 at 9:20
I'm using seq from GNU coreutils.
– John B
Jul 8 '14 at 11:38
1
@JohnB: BSDseq
is being cleverly repurposed here to replicate strings: the format string passed to-f
- normally used to format the numbers being generated - contains only the string to replicate here so that the output contains copies of that string only. Unfortunately, GNUseq
insists on the presence of a number format in the format string, which is the error you're seeing.
– mklement0
Apr 29 '15 at 17:18
Nicely done; also works with multi-characters strings. Please use"$1"
(double quotes), so you can also pass in characters such as'*'
and strings with embedded whitespace. Finally, if you want to be able to use%
, you have to double it (otherwiseseq
will think it's part of a format specification such as%f
); using"${1//%/%%}"
would take care of that. Since (as you mention) you're using BSDseq
, this will work on BSD-like OSs in general (e.g., FreeBSD) - by contrast, it won't work on Linux, where GNUseq
is used.
– mklement0
Apr 29 '15 at 17:30
7
7
This gives me
seq: format ‘#’ has no % directive
. seq
is for numbers, not strings. See gnu.org/software/coreutils/manual/html_node/seq-invocation.html– John B
Jul 7 '14 at 8:51
This gives me
seq: format ‘#’ has no % directive
. seq
is for numbers, not strings. See gnu.org/software/coreutils/manual/html_node/seq-invocation.html– John B
Jul 7 '14 at 8:51
Ah, so I was using the BSD version of seq found on OS X. I'll update the answer. Which version are you using?
– Sam Salisbury
Jul 8 '14 at 9:20
Ah, so I was using the BSD version of seq found on OS X. I'll update the answer. Which version are you using?
– Sam Salisbury
Jul 8 '14 at 9:20
I'm using seq from GNU coreutils.
– John B
Jul 8 '14 at 11:38
I'm using seq from GNU coreutils.
– John B
Jul 8 '14 at 11:38
1
1
@JohnB: BSD
seq
is being cleverly repurposed here to replicate strings: the format string passed to -f
- normally used to format the numbers being generated - contains only the string to replicate here so that the output contains copies of that string only. Unfortunately, GNU seq
insists on the presence of a number format in the format string, which is the error you're seeing.– mklement0
Apr 29 '15 at 17:18
@JohnB: BSD
seq
is being cleverly repurposed here to replicate strings: the format string passed to -f
- normally used to format the numbers being generated - contains only the string to replicate here so that the output contains copies of that string only. Unfortunately, GNU seq
insists on the presence of a number format in the format string, which is the error you're seeing.– mklement0
Apr 29 '15 at 17:18
Nicely done; also works with multi-characters strings. Please use
"$1"
(double quotes), so you can also pass in characters such as '*'
and strings with embedded whitespace. Finally, if you want to be able to use %
, you have to double it (otherwise seq
will think it's part of a format specification such as %f
); using "${1//%/%%}"
would take care of that. Since (as you mention) you're using BSD seq
, this will work on BSD-like OSs in general (e.g., FreeBSD) - by contrast, it won't work on Linux, where GNU seq
is used.– mklement0
Apr 29 '15 at 17:30
Nicely done; also works with multi-characters strings. Please use
"$1"
(double quotes), so you can also pass in characters such as '*'
and strings with embedded whitespace. Finally, if you want to be able to use %
, you have to double it (otherwise seq
will think it's part of a format specification such as %f
); using "${1//%/%%}"
would take care of that. Since (as you mention) you're using BSD seq
, this will work on BSD-like OSs in general (e.g., FreeBSD) - by contrast, it won't work on Linux, where GNU seq
is used.– mklement0
Apr 29 '15 at 17:30
|
show 2 more comments
Here's two interesting ways:
ubuntu@ubuntu:~$ yes = | head -10 | paste -s -d '' -
==========
ubuntu@ubuntu:~$ yes = | head -10 | tr -d "n"
==========ubuntu@ubuntu:~$
Note these two are subtly different - The paste
method ends in a new line. The tr
method does not.
1
Nicely done; please note that BSDpaste
inexplicably requires-d ''
for specifying an empty delimiter, and fails with-d ''
--d ''
should work wit all POSIX-compatiblepaste
implementations and indeed works with GNUpaste
too.
– mklement0
Apr 29 '15 at 13:56
Similar in spirit, with fewer outboard tools:yes | mapfile -n 100 -C 'printf = #' -c 1
– bishop
Apr 27 '16 at 19:42
@bishop: While your command indeed creates one fewer subshell, it is still slower for higher repeat counts, and for lower repeat counts the difference probably doesn't matter; the exact threshold is probably both hardware- and OS-dependent, e.g., on my OSX 10.11.5 machine this answer is already faster at 500; trytime yes = | head -500 | paste -s -d '' -; time yes | mapfile -n 500 -C 'printf = #' -c 1
. More importantly, however: if you're usingprintf
anyway, you may as well go with the both simpler and more efficient approach from the accepted answer:printf '%.s=' $(seq 500)
– mklement0
Jul 17 '16 at 6:44
add a comment |
Here's two interesting ways:
ubuntu@ubuntu:~$ yes = | head -10 | paste -s -d '' -
==========
ubuntu@ubuntu:~$ yes = | head -10 | tr -d "n"
==========ubuntu@ubuntu:~$
Note these two are subtly different - The paste
method ends in a new line. The tr
method does not.
1
Nicely done; please note that BSDpaste
inexplicably requires-d ''
for specifying an empty delimiter, and fails with-d ''
--d ''
should work wit all POSIX-compatiblepaste
implementations and indeed works with GNUpaste
too.
– mklement0
Apr 29 '15 at 13:56
Similar in spirit, with fewer outboard tools:yes | mapfile -n 100 -C 'printf = #' -c 1
– bishop
Apr 27 '16 at 19:42
@bishop: While your command indeed creates one fewer subshell, it is still slower for higher repeat counts, and for lower repeat counts the difference probably doesn't matter; the exact threshold is probably both hardware- and OS-dependent, e.g., on my OSX 10.11.5 machine this answer is already faster at 500; trytime yes = | head -500 | paste -s -d '' -; time yes | mapfile -n 500 -C 'printf = #' -c 1
. More importantly, however: if you're usingprintf
anyway, you may as well go with the both simpler and more efficient approach from the accepted answer:printf '%.s=' $(seq 500)
– mklement0
Jul 17 '16 at 6:44
add a comment |
Here's two interesting ways:
ubuntu@ubuntu:~$ yes = | head -10 | paste -s -d '' -
==========
ubuntu@ubuntu:~$ yes = | head -10 | tr -d "n"
==========ubuntu@ubuntu:~$
Note these two are subtly different - The paste
method ends in a new line. The tr
method does not.
Here's two interesting ways:
ubuntu@ubuntu:~$ yes = | head -10 | paste -s -d '' -
==========
ubuntu@ubuntu:~$ yes = | head -10 | tr -d "n"
==========ubuntu@ubuntu:~$
Note these two are subtly different - The paste
method ends in a new line. The tr
method does not.
answered Nov 21 '13 at 15:37


Digital TraumaDigital Trauma
11.4k13369
11.4k13369
1
Nicely done; please note that BSDpaste
inexplicably requires-d ''
for specifying an empty delimiter, and fails with-d ''
--d ''
should work wit all POSIX-compatiblepaste
implementations and indeed works with GNUpaste
too.
– mklement0
Apr 29 '15 at 13:56
Similar in spirit, with fewer outboard tools:yes | mapfile -n 100 -C 'printf = #' -c 1
– bishop
Apr 27 '16 at 19:42
@bishop: While your command indeed creates one fewer subshell, it is still slower for higher repeat counts, and for lower repeat counts the difference probably doesn't matter; the exact threshold is probably both hardware- and OS-dependent, e.g., on my OSX 10.11.5 machine this answer is already faster at 500; trytime yes = | head -500 | paste -s -d '' -; time yes | mapfile -n 500 -C 'printf = #' -c 1
. More importantly, however: if you're usingprintf
anyway, you may as well go with the both simpler and more efficient approach from the accepted answer:printf '%.s=' $(seq 500)
– mklement0
Jul 17 '16 at 6:44
add a comment |
1
Nicely done; please note that BSDpaste
inexplicably requires-d ''
for specifying an empty delimiter, and fails with-d ''
--d ''
should work wit all POSIX-compatiblepaste
implementations and indeed works with GNUpaste
too.
– mklement0
Apr 29 '15 at 13:56
Similar in spirit, with fewer outboard tools:yes | mapfile -n 100 -C 'printf = #' -c 1
– bishop
Apr 27 '16 at 19:42
@bishop: While your command indeed creates one fewer subshell, it is still slower for higher repeat counts, and for lower repeat counts the difference probably doesn't matter; the exact threshold is probably both hardware- and OS-dependent, e.g., on my OSX 10.11.5 machine this answer is already faster at 500; trytime yes = | head -500 | paste -s -d '' -; time yes | mapfile -n 500 -C 'printf = #' -c 1
. More importantly, however: if you're usingprintf
anyway, you may as well go with the both simpler and more efficient approach from the accepted answer:printf '%.s=' $(seq 500)
– mklement0
Jul 17 '16 at 6:44
1
1
Nicely done; please note that BSD
paste
inexplicably requires -d ''
for specifying an empty delimiter, and fails with -d ''
- -d ''
should work wit all POSIX-compatible paste
implementations and indeed works with GNU paste
too.– mklement0
Apr 29 '15 at 13:56
Nicely done; please note that BSD
paste
inexplicably requires -d ''
for specifying an empty delimiter, and fails with -d ''
- -d ''
should work wit all POSIX-compatible paste
implementations and indeed works with GNU paste
too.– mklement0
Apr 29 '15 at 13:56
Similar in spirit, with fewer outboard tools:
yes | mapfile -n 100 -C 'printf = #' -c 1
– bishop
Apr 27 '16 at 19:42
Similar in spirit, with fewer outboard tools:
yes | mapfile -n 100 -C 'printf = #' -c 1
– bishop
Apr 27 '16 at 19:42
@bishop: While your command indeed creates one fewer subshell, it is still slower for higher repeat counts, and for lower repeat counts the difference probably doesn't matter; the exact threshold is probably both hardware- and OS-dependent, e.g., on my OSX 10.11.5 machine this answer is already faster at 500; try
time yes = | head -500 | paste -s -d '' -; time yes | mapfile -n 500 -C 'printf = #' -c 1
. More importantly, however: if you're using printf
anyway, you may as well go with the both simpler and more efficient approach from the accepted answer: printf '%.s=' $(seq 500)
– mklement0
Jul 17 '16 at 6:44
@bishop: While your command indeed creates one fewer subshell, it is still slower for higher repeat counts, and for lower repeat counts the difference probably doesn't matter; the exact threshold is probably both hardware- and OS-dependent, e.g., on my OSX 10.11.5 machine this answer is already faster at 500; try
time yes = | head -500 | paste -s -d '' -; time yes | mapfile -n 500 -C 'printf = #' -c 1
. More importantly, however: if you're using printf
anyway, you may as well go with the both simpler and more efficient approach from the accepted answer: printf '%.s=' $(seq 500)
– mklement0
Jul 17 '16 at 6:44
add a comment |
There is no simple way. Avoid loops using printf
and substitution.
str=$(printf "%40s")
echo ${str// /rep}
# echoes "rep" 40 times.
2
Nice, but only performs reasonably with small repeat counts. Here's a function wrapper that can be invoked asrepl = 100
, for instance (doesn't output a trailingn
):repl() { local ts=$(printf "%${2}s"); printf %s "${ts// /$1}"; }
– mklement0
Dec 7 '13 at 18:42
1
@mklement0 Nice of you to provide function versions of both solutions, +1 on both!
– Camilo Martin
Jan 2 '14 at 12:16
add a comment |
There is no simple way. Avoid loops using printf
and substitution.
str=$(printf "%40s")
echo ${str// /rep}
# echoes "rep" 40 times.
2
Nice, but only performs reasonably with small repeat counts. Here's a function wrapper that can be invoked asrepl = 100
, for instance (doesn't output a trailingn
):repl() { local ts=$(printf "%${2}s"); printf %s "${ts// /$1}"; }
– mklement0
Dec 7 '13 at 18:42
1
@mklement0 Nice of you to provide function versions of both solutions, +1 on both!
– Camilo Martin
Jan 2 '14 at 12:16
add a comment |
There is no simple way. Avoid loops using printf
and substitution.
str=$(printf "%40s")
echo ${str// /rep}
# echoes "rep" 40 times.
There is no simple way. Avoid loops using printf
and substitution.
str=$(printf "%40s")
echo ${str// /rep}
# echoes "rep" 40 times.
edited Jun 1 '14 at 7:10
Steven Penny
1
1
answered Mar 18 '11 at 8:54
TimTim
9,90755393
9,90755393
2
Nice, but only performs reasonably with small repeat counts. Here's a function wrapper that can be invoked asrepl = 100
, for instance (doesn't output a trailingn
):repl() { local ts=$(printf "%${2}s"); printf %s "${ts// /$1}"; }
– mklement0
Dec 7 '13 at 18:42
1
@mklement0 Nice of you to provide function versions of both solutions, +1 on both!
– Camilo Martin
Jan 2 '14 at 12:16
add a comment |
2
Nice, but only performs reasonably with small repeat counts. Here's a function wrapper that can be invoked asrepl = 100
, for instance (doesn't output a trailingn
):repl() { local ts=$(printf "%${2}s"); printf %s "${ts// /$1}"; }
– mklement0
Dec 7 '13 at 18:42
1
@mklement0 Nice of you to provide function versions of both solutions, +1 on both!
– Camilo Martin
Jan 2 '14 at 12:16
2
2
Nice, but only performs reasonably with small repeat counts. Here's a function wrapper that can be invoked as
repl = 100
, for instance (doesn't output a trailing n
): repl() { local ts=$(printf "%${2}s"); printf %s "${ts// /$1}"; }
– mklement0
Dec 7 '13 at 18:42
Nice, but only performs reasonably with small repeat counts. Here's a function wrapper that can be invoked as
repl = 100
, for instance (doesn't output a trailing n
): repl() { local ts=$(printf "%${2}s"); printf %s "${ts// /$1}"; }
– mklement0
Dec 7 '13 at 18:42
1
1
@mklement0 Nice of you to provide function versions of both solutions, +1 on both!
– Camilo Martin
Jan 2 '14 at 12:16
@mklement0 Nice of you to provide function versions of both solutions, +1 on both!
– Camilo Martin
Jan 2 '14 at 12:16
add a comment |
#!/usr/bin/awk -f
BEGIN {
OFS = "="
NF = 100
print
}
Or
#!/usr/bin/awk -f
BEGIN {
while (z++ < 100) printf "="
}
Example
3
Nicely done; this is POSIX-compliant and reasonably fast even with high repeat counts, while also supporting multi-character strings. Here's the shell version:awk 'BEGIN { while (c++ < 100) printf "=" }'
. Wrapped into a parameterized shell function (invoke asrepeat 100 =
, for instance):repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { txt=substr(txt, 2); while (i++ < count) printf txt }'; }
. (The dummy.
prefix char and complementarysubstr
call are needed to work around a bug in BSDawk
, where passing a variable value that starts with=
breaks the command.)
– mklement0
Apr 29 '15 at 18:12
1
TheNF = 100
solution is very clever (though to get 100=
, you must useNF = 101
). The caveats are that it crashes BSDawk
(but it's very fast withgawk
and even faster withmawk
), and that POSIX discusses neither assigning toNF
, nor use of fields inBEGIN
blocks. You can make it work in BSDawk
as well with a slight tweak:awk 'BEGIN { OFS = "="; $101=""; print }'
(but curiously, in BSDawk
that isn't faster than the loop solution). As a parameterized shell solution:repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { OFS=substr(txt, 2); $(count+1)=""; print }'; }
.
– mklement0
May 14 '15 at 19:01
Note to users - The NF=100 trick causes a segment fault on older awk. Theoriginal-awk
is the name under Linux of the older awk similar to BSD's awk, which has also been reported to crash, if you want to try this. Note that crashing is usually the first step toward finding an exploitable bug. This answer is so promoting insecure code.
– user2350426
Aug 25 '15 at 4:54
2
Note to users -original-awk
is non standard and not recommended
– Steven Penny
Aug 25 '15 at 23:02
An alternative to the first code snippet can beawk NF=100 OFS='=' <<< ""
(usingbash
andgawk
)
– oliv
May 24 '18 at 13:16
add a comment |
#!/usr/bin/awk -f
BEGIN {
OFS = "="
NF = 100
print
}
Or
#!/usr/bin/awk -f
BEGIN {
while (z++ < 100) printf "="
}
Example
3
Nicely done; this is POSIX-compliant and reasonably fast even with high repeat counts, while also supporting multi-character strings. Here's the shell version:awk 'BEGIN { while (c++ < 100) printf "=" }'
. Wrapped into a parameterized shell function (invoke asrepeat 100 =
, for instance):repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { txt=substr(txt, 2); while (i++ < count) printf txt }'; }
. (The dummy.
prefix char and complementarysubstr
call are needed to work around a bug in BSDawk
, where passing a variable value that starts with=
breaks the command.)
– mklement0
Apr 29 '15 at 18:12
1
TheNF = 100
solution is very clever (though to get 100=
, you must useNF = 101
). The caveats are that it crashes BSDawk
(but it's very fast withgawk
and even faster withmawk
), and that POSIX discusses neither assigning toNF
, nor use of fields inBEGIN
blocks. You can make it work in BSDawk
as well with a slight tweak:awk 'BEGIN { OFS = "="; $101=""; print }'
(but curiously, in BSDawk
that isn't faster than the loop solution). As a parameterized shell solution:repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { OFS=substr(txt, 2); $(count+1)=""; print }'; }
.
– mklement0
May 14 '15 at 19:01
Note to users - The NF=100 trick causes a segment fault on older awk. Theoriginal-awk
is the name under Linux of the older awk similar to BSD's awk, which has also been reported to crash, if you want to try this. Note that crashing is usually the first step toward finding an exploitable bug. This answer is so promoting insecure code.
– user2350426
Aug 25 '15 at 4:54
2
Note to users -original-awk
is non standard and not recommended
– Steven Penny
Aug 25 '15 at 23:02
An alternative to the first code snippet can beawk NF=100 OFS='=' <<< ""
(usingbash
andgawk
)
– oliv
May 24 '18 at 13:16
add a comment |
#!/usr/bin/awk -f
BEGIN {
OFS = "="
NF = 100
print
}
Or
#!/usr/bin/awk -f
BEGIN {
while (z++ < 100) printf "="
}
Example
#!/usr/bin/awk -f
BEGIN {
OFS = "="
NF = 100
print
}
Or
#!/usr/bin/awk -f
BEGIN {
while (z++ < 100) printf "="
}
Example
edited May 14 '15 at 5:51
answered Jun 1 '14 at 8:28
Steven PennySteven Penny
1
1
3
Nicely done; this is POSIX-compliant and reasonably fast even with high repeat counts, while also supporting multi-character strings. Here's the shell version:awk 'BEGIN { while (c++ < 100) printf "=" }'
. Wrapped into a parameterized shell function (invoke asrepeat 100 =
, for instance):repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { txt=substr(txt, 2); while (i++ < count) printf txt }'; }
. (The dummy.
prefix char and complementarysubstr
call are needed to work around a bug in BSDawk
, where passing a variable value that starts with=
breaks the command.)
– mklement0
Apr 29 '15 at 18:12
1
TheNF = 100
solution is very clever (though to get 100=
, you must useNF = 101
). The caveats are that it crashes BSDawk
(but it's very fast withgawk
and even faster withmawk
), and that POSIX discusses neither assigning toNF
, nor use of fields inBEGIN
blocks. You can make it work in BSDawk
as well with a slight tweak:awk 'BEGIN { OFS = "="; $101=""; print }'
(but curiously, in BSDawk
that isn't faster than the loop solution). As a parameterized shell solution:repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { OFS=substr(txt, 2); $(count+1)=""; print }'; }
.
– mklement0
May 14 '15 at 19:01
Note to users - The NF=100 trick causes a segment fault on older awk. Theoriginal-awk
is the name under Linux of the older awk similar to BSD's awk, which has also been reported to crash, if you want to try this. Note that crashing is usually the first step toward finding an exploitable bug. This answer is so promoting insecure code.
– user2350426
Aug 25 '15 at 4:54
2
Note to users -original-awk
is non standard and not recommended
– Steven Penny
Aug 25 '15 at 23:02
An alternative to the first code snippet can beawk NF=100 OFS='=' <<< ""
(usingbash
andgawk
)
– oliv
May 24 '18 at 13:16
add a comment |
3
Nicely done; this is POSIX-compliant and reasonably fast even with high repeat counts, while also supporting multi-character strings. Here's the shell version:awk 'BEGIN { while (c++ < 100) printf "=" }'
. Wrapped into a parameterized shell function (invoke asrepeat 100 =
, for instance):repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { txt=substr(txt, 2); while (i++ < count) printf txt }'; }
. (The dummy.
prefix char and complementarysubstr
call are needed to work around a bug in BSDawk
, where passing a variable value that starts with=
breaks the command.)
– mklement0
Apr 29 '15 at 18:12
1
TheNF = 100
solution is very clever (though to get 100=
, you must useNF = 101
). The caveats are that it crashes BSDawk
(but it's very fast withgawk
and even faster withmawk
), and that POSIX discusses neither assigning toNF
, nor use of fields inBEGIN
blocks. You can make it work in BSDawk
as well with a slight tweak:awk 'BEGIN { OFS = "="; $101=""; print }'
(but curiously, in BSDawk
that isn't faster than the loop solution). As a parameterized shell solution:repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { OFS=substr(txt, 2); $(count+1)=""; print }'; }
.
– mklement0
May 14 '15 at 19:01
Note to users - The NF=100 trick causes a segment fault on older awk. Theoriginal-awk
is the name under Linux of the older awk similar to BSD's awk, which has also been reported to crash, if you want to try this. Note that crashing is usually the first step toward finding an exploitable bug. This answer is so promoting insecure code.
– user2350426
Aug 25 '15 at 4:54
2
Note to users -original-awk
is non standard and not recommended
– Steven Penny
Aug 25 '15 at 23:02
An alternative to the first code snippet can beawk NF=100 OFS='=' <<< ""
(usingbash
andgawk
)
– oliv
May 24 '18 at 13:16
3
3
Nicely done; this is POSIX-compliant and reasonably fast even with high repeat counts, while also supporting multi-character strings. Here's the shell version:
awk 'BEGIN { while (c++ < 100) printf "=" }'
. Wrapped into a parameterized shell function (invoke as repeat 100 =
, for instance): repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { txt=substr(txt, 2); while (i++ < count) printf txt }'; }
. (The dummy .
prefix char and complementary substr
call are needed to work around a bug in BSD awk
, where passing a variable value that starts with =
breaks the command.)– mklement0
Apr 29 '15 at 18:12
Nicely done; this is POSIX-compliant and reasonably fast even with high repeat counts, while also supporting multi-character strings. Here's the shell version:
awk 'BEGIN { while (c++ < 100) printf "=" }'
. Wrapped into a parameterized shell function (invoke as repeat 100 =
, for instance): repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { txt=substr(txt, 2); while (i++ < count) printf txt }'; }
. (The dummy .
prefix char and complementary substr
call are needed to work around a bug in BSD awk
, where passing a variable value that starts with =
breaks the command.)– mklement0
Apr 29 '15 at 18:12
1
1
The
NF = 100
solution is very clever (though to get 100 =
, you must use NF = 101
). The caveats are that it crashes BSD awk
(but it's very fast with gawk
and even faster with mawk
), and that POSIX discusses neither assigning to NF
, nor use of fields in BEGIN
blocks. You can make it work in BSD awk
as well with a slight tweak: awk 'BEGIN { OFS = "="; $101=""; print }'
(but curiously, in BSD awk
that isn't faster than the loop solution). As a parameterized shell solution: repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { OFS=substr(txt, 2); $(count+1)=""; print }'; }
.– mklement0
May 14 '15 at 19:01
The
NF = 100
solution is very clever (though to get 100 =
, you must use NF = 101
). The caveats are that it crashes BSD awk
(but it's very fast with gawk
and even faster with mawk
), and that POSIX discusses neither assigning to NF
, nor use of fields in BEGIN
blocks. You can make it work in BSD awk
as well with a slight tweak: awk 'BEGIN { OFS = "="; $101=""; print }'
(but curiously, in BSD awk
that isn't faster than the loop solution). As a parameterized shell solution: repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { OFS=substr(txt, 2); $(count+1)=""; print }'; }
.– mklement0
May 14 '15 at 19:01
Note to users - The NF=100 trick causes a segment fault on older awk. The
original-awk
is the name under Linux of the older awk similar to BSD's awk, which has also been reported to crash, if you want to try this. Note that crashing is usually the first step toward finding an exploitable bug. This answer is so promoting insecure code.– user2350426
Aug 25 '15 at 4:54
Note to users - The NF=100 trick causes a segment fault on older awk. The
original-awk
is the name under Linux of the older awk similar to BSD's awk, which has also been reported to crash, if you want to try this. Note that crashing is usually the first step toward finding an exploitable bug. This answer is so promoting insecure code.– user2350426
Aug 25 '15 at 4:54
2
2
Note to users -
original-awk
is non standard and not recommended– Steven Penny
Aug 25 '15 at 23:02
Note to users -
original-awk
is non standard and not recommended– Steven Penny
Aug 25 '15 at 23:02
An alternative to the first code snippet can be
awk NF=100 OFS='=' <<< ""
(using bash
and gawk
)– oliv
May 24 '18 at 13:16
An alternative to the first code snippet can be
awk NF=100 OFS='=' <<< ""
(using bash
and gawk
)– oliv
May 24 '18 at 13:16
add a comment |
If you want POSIX-compliance and consistency across different implementations of echo
and printf
, and/or shells other than just 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)
...will produce the same output as perl -E 'say "=" x 100'
just about everywhere.
1
The problem is thatseq
is not a POSIX utility (though BSD and Linux systems have implementations of it) - you can do POSIX shell arithmetic with awhile
loop instead, as in @Xennex81's answer (withprintf "="
, as you correctly suggest, rather thanecho -n
).
– mklement0
Apr 29 '15 at 17:56
1
Oops, you're quite right. Things like that just slip past me sometimes as that standard makes no f'ing sense.cal
is POSIX.seq
is not. Anyway, rather than rewrite the answer with a while loop (as you say, that's already in other answers) I'll add a RYO function. More educational that way ;-).
– Geoff Nixon
May 3 '15 at 14:52
add a comment |
If you want POSIX-compliance and consistency across different implementations of echo
and printf
, and/or shells other than just 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)
...will produce the same output as perl -E 'say "=" x 100'
just about everywhere.
1
The problem is thatseq
is not a POSIX utility (though BSD and Linux systems have implementations of it) - you can do POSIX shell arithmetic with awhile
loop instead, as in @Xennex81's answer (withprintf "="
, as you correctly suggest, rather thanecho -n
).
– mklement0
Apr 29 '15 at 17:56
1
Oops, you're quite right. Things like that just slip past me sometimes as that standard makes no f'ing sense.cal
is POSIX.seq
is not. Anyway, rather than rewrite the answer with a while loop (as you say, that's already in other answers) I'll add a RYO function. More educational that way ;-).
– Geoff Nixon
May 3 '15 at 14:52
add a comment |
If you want POSIX-compliance and consistency across different implementations of echo
and printf
, and/or shells other than just 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)
...will produce the same output as perl -E 'say "=" x 100'
just about everywhere.
If you want POSIX-compliance and consistency across different implementations of echo
and printf
, and/or shells other than just 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)
...will produce the same output as perl -E 'say "=" x 100'
just about everywhere.
edited May 3 '15 at 14:58
answered Dec 7 '14 at 9:34


Geoff NixonGeoff Nixon
1,88211527
1,88211527
1
The problem is thatseq
is not a POSIX utility (though BSD and Linux systems have implementations of it) - you can do POSIX shell arithmetic with awhile
loop instead, as in @Xennex81's answer (withprintf "="
, as you correctly suggest, rather thanecho -n
).
– mklement0
Apr 29 '15 at 17:56
1
Oops, you're quite right. Things like that just slip past me sometimes as that standard makes no f'ing sense.cal
is POSIX.seq
is not. Anyway, rather than rewrite the answer with a while loop (as you say, that's already in other answers) I'll add a RYO function. More educational that way ;-).
– Geoff Nixon
May 3 '15 at 14:52
add a comment |
1
The problem is thatseq
is not a POSIX utility (though BSD and Linux systems have implementations of it) - you can do POSIX shell arithmetic with awhile
loop instead, as in @Xennex81's answer (withprintf "="
, as you correctly suggest, rather thanecho -n
).
– mklement0
Apr 29 '15 at 17:56
1
Oops, you're quite right. Things like that just slip past me sometimes as that standard makes no f'ing sense.cal
is POSIX.seq
is not. Anyway, rather than rewrite the answer with a while loop (as you say, that's already in other answers) I'll add a RYO function. More educational that way ;-).
– Geoff Nixon
May 3 '15 at 14:52
1
1
The problem is that
seq
is not a POSIX utility (though BSD and Linux systems have implementations of it) - you can do POSIX shell arithmetic with a while
loop instead, as in @Xennex81's answer (with printf "="
, as you correctly suggest, rather than echo -n
).– mklement0
Apr 29 '15 at 17:56
The problem is that
seq
is not a POSIX utility (though BSD and Linux systems have implementations of it) - you can do POSIX shell arithmetic with a while
loop instead, as in @Xennex81's answer (with printf "="
, as you correctly suggest, rather than echo -n
).– mklement0
Apr 29 '15 at 17:56
1
1
Oops, you're quite right. Things like that just slip past me sometimes as that standard makes no f'ing sense.
cal
is POSIX. seq
is not. Anyway, rather than rewrite the answer with a while loop (as you say, that's already in other answers) I'll add a RYO function. More educational that way ;-).– Geoff Nixon
May 3 '15 at 14:52
Oops, you're quite right. Things like that just slip past me sometimes as that standard makes no f'ing sense.
cal
is POSIX. seq
is not. Anyway, rather than rewrite the answer with a while loop (as you say, that's already in other answers) I'll add a RYO function. More educational that way ;-).– Geoff Nixon
May 3 '15 at 14:52
add a comment |
I guess the original purpose of the question was to do this just with the shell's built-in commands. So for
loops and printf
s would be legitimate, while rep
, perl
, and also jot
below would not. Still, the following command
jot -s "/" -b "\" $((COLUMNS/2))
for instance, prints a window-wide line of ////////////
2
Nicely done; this works well even with high repeat counts (while also supporting multi-character strings). To better illustrate the approach, here's the equivalent of the OP's command:jot -s '' -b '=' 100
. The caveat is that while BSD-like platforms, including OSX, come withjot
, Linux distros do not.
– mklement0
Apr 29 '15 at 17:49
1
Thanks, I like your use of -s '' even better. I've changed my scripts.
– Stefan Ludwig
Apr 29 '15 at 21:47
On recent Debian-based systems,apt install athena-jot
would providejot
.
– agc
Feb 8 at 20:54
add a comment |
I guess the original purpose of the question was to do this just with the shell's built-in commands. So for
loops and printf
s would be legitimate, while rep
, perl
, and also jot
below would not. Still, the following command
jot -s "/" -b "\" $((COLUMNS/2))
for instance, prints a window-wide line of ////////////
2
Nicely done; this works well even with high repeat counts (while also supporting multi-character strings). To better illustrate the approach, here's the equivalent of the OP's command:jot -s '' -b '=' 100
. The caveat is that while BSD-like platforms, including OSX, come withjot
, Linux distros do not.
– mklement0
Apr 29 '15 at 17:49
1
Thanks, I like your use of -s '' even better. I've changed my scripts.
– Stefan Ludwig
Apr 29 '15 at 21:47
On recent Debian-based systems,apt install athena-jot
would providejot
.
– agc
Feb 8 at 20:54
add a comment |
I guess the original purpose of the question was to do this just with the shell's built-in commands. So for
loops and printf
s would be legitimate, while rep
, perl
, and also jot
below would not. Still, the following command
jot -s "/" -b "\" $((COLUMNS/2))
for instance, prints a window-wide line of ////////////
I guess the original purpose of the question was to do this just with the shell's built-in commands. So for
loops and printf
s would be legitimate, while rep
, perl
, and also jot
below would not. Still, the following command
jot -s "/" -b "\" $((COLUMNS/2))
for instance, prints a window-wide line of ////////////
answered Nov 21 '13 at 14:54
Stefan LudwigStefan Ludwig
1287
1287
2
Nicely done; this works well even with high repeat counts (while also supporting multi-character strings). To better illustrate the approach, here's the equivalent of the OP's command:jot -s '' -b '=' 100
. The caveat is that while BSD-like platforms, including OSX, come withjot
, Linux distros do not.
– mklement0
Apr 29 '15 at 17:49
1
Thanks, I like your use of -s '' even better. I've changed my scripts.
– Stefan Ludwig
Apr 29 '15 at 21:47
On recent Debian-based systems,apt install athena-jot
would providejot
.
– agc
Feb 8 at 20:54
add a comment |
2
Nicely done; this works well even with high repeat counts (while also supporting multi-character strings). To better illustrate the approach, here's the equivalent of the OP's command:jot -s '' -b '=' 100
. The caveat is that while BSD-like platforms, including OSX, come withjot
, Linux distros do not.
– mklement0
Apr 29 '15 at 17:49
1
Thanks, I like your use of -s '' even better. I've changed my scripts.
– Stefan Ludwig
Apr 29 '15 at 21:47
On recent Debian-based systems,apt install athena-jot
would providejot
.
– agc
Feb 8 at 20:54
2
2
Nicely done; this works well even with high repeat counts (while also supporting multi-character strings). To better illustrate the approach, here's the equivalent of the OP's command:
jot -s '' -b '=' 100
. The caveat is that while BSD-like platforms, including OSX, come with jot
, Linux distros do not.– mklement0
Apr 29 '15 at 17:49
Nicely done; this works well even with high repeat counts (while also supporting multi-character strings). To better illustrate the approach, here's the equivalent of the OP's command:
jot -s '' -b '=' 100
. The caveat is that while BSD-like platforms, including OSX, come with jot
, Linux distros do not.– mklement0
Apr 29 '15 at 17:49
1
1
Thanks, I like your use of -s '' even better. I've changed my scripts.
– Stefan Ludwig
Apr 29 '15 at 21:47
Thanks, I like your use of -s '' even better. I've changed my scripts.
– Stefan Ludwig
Apr 29 '15 at 21:47
On recent Debian-based systems,
apt install athena-jot
would provide jot
.– agc
Feb 8 at 20:54
On recent Debian-based systems,
apt install athena-jot
would provide jot
.– agc
Feb 8 at 20:54
add a comment |
As others have said, in bash brace expansion precedes parameter expansion, so {m,n}
ranges can only contain literals. seq
and jot
provide clean solutions but aren't fully portable from one system to another, even if you're using the same shell on each. (Though seq
is increasingly available; e.g., in FreeBSD 9.3 and higher.) eval
and other forms of indirection always work but are somewhat inelegant.
Fortunately, bash supports C-style for loops (with arithmetic expressions only). So here's a concise "pure bash" way:
repecho() { for ((i=0; i<$1; ++i)); do echo -n "$2"; done; echo; }
This takes the number of repetitions as the first argument and the string to be repeated (which may be a single character, as in the problem description) as the second argument. repecho 7 b
outputs bbbbbbb
(terminated by a newline).
Dennis Williamson gave essentially this solution four years ago in his excellent answer to Creating string of repeated characters in shell script. My function body differs slightly from the code there:
Since the focus here is on repeating a single character and the shell is bash, it's probably safe to use
echo
instead ofprintf
. And I read the problem description in this question as expressing a preference to print withecho
. The above function definition works in bash and ksh93. Althoughprintf
is more portable (and should usually be used for this sort of thing),echo
's syntax is arguably more readable.
Some shells'
echo
builtins interpret-
by itself as an option--even though the usual meaning of-
, to use stdin for input, is nonsensical forecho
. zsh does this. And there definitely existecho
s that don't recognize-n
, as it is not standard. (Many Bourne-style shells don't accept C-style for loops at all, thus theirecho
behavior needn't be considered..)
Here the task is to print the sequence; there, it was to assign it to a variable.
If $n
is the desired number of repetitions and you don't have to reuse it, and you want something even shorter:
while ((n--)); do echo -n "$s"; done; echo
n
must be a variable--this way doesn't work with positional parameters. $s
is the text to be repeated.
1
Strongly avoid doing loop versions.printf "%100s" | tr ' ' '='
is optimal.
– ocodo
Nov 4 '14 at 5:48
Good background info and kudos for packaging the functionality as a function, which works inzsh
as well, incidentally. The echo-in-a-loop approach works well for smaller repeat counts, but for larger ones there are POSIX-compliant alternatives based on utilities, as evidenced by @Slomojo's comment.
– mklement0
Apr 29 '15 at 15:53
Adding parentheses around your shorter loop preserves the value of n without affecting the echos:(while ((n--)); do echo -n "$s"; done; echo)
– user2350426
Aug 23 '15 at 4:24
add a comment |
As others have said, in bash brace expansion precedes parameter expansion, so {m,n}
ranges can only contain literals. seq
and jot
provide clean solutions but aren't fully portable from one system to another, even if you're using the same shell on each. (Though seq
is increasingly available; e.g., in FreeBSD 9.3 and higher.) eval
and other forms of indirection always work but are somewhat inelegant.
Fortunately, bash supports C-style for loops (with arithmetic expressions only). So here's a concise "pure bash" way:
repecho() { for ((i=0; i<$1; ++i)); do echo -n "$2"; done; echo; }
This takes the number of repetitions as the first argument and the string to be repeated (which may be a single character, as in the problem description) as the second argument. repecho 7 b
outputs bbbbbbb
(terminated by a newline).
Dennis Williamson gave essentially this solution four years ago in his excellent answer to Creating string of repeated characters in shell script. My function body differs slightly from the code there:
Since the focus here is on repeating a single character and the shell is bash, it's probably safe to use
echo
instead ofprintf
. And I read the problem description in this question as expressing a preference to print withecho
. The above function definition works in bash and ksh93. Althoughprintf
is more portable (and should usually be used for this sort of thing),echo
's syntax is arguably more readable.
Some shells'
echo
builtins interpret-
by itself as an option--even though the usual meaning of-
, to use stdin for input, is nonsensical forecho
. zsh does this. And there definitely existecho
s that don't recognize-n
, as it is not standard. (Many Bourne-style shells don't accept C-style for loops at all, thus theirecho
behavior needn't be considered..)
Here the task is to print the sequence; there, it was to assign it to a variable.
If $n
is the desired number of repetitions and you don't have to reuse it, and you want something even shorter:
while ((n--)); do echo -n "$s"; done; echo
n
must be a variable--this way doesn't work with positional parameters. $s
is the text to be repeated.
1
Strongly avoid doing loop versions.printf "%100s" | tr ' ' '='
is optimal.
– ocodo
Nov 4 '14 at 5:48
Good background info and kudos for packaging the functionality as a function, which works inzsh
as well, incidentally. The echo-in-a-loop approach works well for smaller repeat counts, but for larger ones there are POSIX-compliant alternatives based on utilities, as evidenced by @Slomojo's comment.
– mklement0
Apr 29 '15 at 15:53
Adding parentheses around your shorter loop preserves the value of n without affecting the echos:(while ((n--)); do echo -n "$s"; done; echo)
– user2350426
Aug 23 '15 at 4:24
add a comment |
As others have said, in bash brace expansion precedes parameter expansion, so {m,n}
ranges can only contain literals. seq
and jot
provide clean solutions but aren't fully portable from one system to another, even if you're using the same shell on each. (Though seq
is increasingly available; e.g., in FreeBSD 9.3 and higher.) eval
and other forms of indirection always work but are somewhat inelegant.
Fortunately, bash supports C-style for loops (with arithmetic expressions only). So here's a concise "pure bash" way:
repecho() { for ((i=0; i<$1; ++i)); do echo -n "$2"; done; echo; }
This takes the number of repetitions as the first argument and the string to be repeated (which may be a single character, as in the problem description) as the second argument. repecho 7 b
outputs bbbbbbb
(terminated by a newline).
Dennis Williamson gave essentially this solution four years ago in his excellent answer to Creating string of repeated characters in shell script. My function body differs slightly from the code there:
Since the focus here is on repeating a single character and the shell is bash, it's probably safe to use
echo
instead ofprintf
. And I read the problem description in this question as expressing a preference to print withecho
. The above function definition works in bash and ksh93. Althoughprintf
is more portable (and should usually be used for this sort of thing),echo
's syntax is arguably more readable.
Some shells'
echo
builtins interpret-
by itself as an option--even though the usual meaning of-
, to use stdin for input, is nonsensical forecho
. zsh does this. And there definitely existecho
s that don't recognize-n
, as it is not standard. (Many Bourne-style shells don't accept C-style for loops at all, thus theirecho
behavior needn't be considered..)
Here the task is to print the sequence; there, it was to assign it to a variable.
If $n
is the desired number of repetitions and you don't have to reuse it, and you want something even shorter:
while ((n--)); do echo -n "$s"; done; echo
n
must be a variable--this way doesn't work with positional parameters. $s
is the text to be repeated.
As others have said, in bash brace expansion precedes parameter expansion, so {m,n}
ranges can only contain literals. seq
and jot
provide clean solutions but aren't fully portable from one system to another, even if you're using the same shell on each. (Though seq
is increasingly available; e.g., in FreeBSD 9.3 and higher.) eval
and other forms of indirection always work but are somewhat inelegant.
Fortunately, bash supports C-style for loops (with arithmetic expressions only). So here's a concise "pure bash" way:
repecho() { for ((i=0; i<$1; ++i)); do echo -n "$2"; done; echo; }
This takes the number of repetitions as the first argument and the string to be repeated (which may be a single character, as in the problem description) as the second argument. repecho 7 b
outputs bbbbbbb
(terminated by a newline).
Dennis Williamson gave essentially this solution four years ago in his excellent answer to Creating string of repeated characters in shell script. My function body differs slightly from the code there:
Since the focus here is on repeating a single character and the shell is bash, it's probably safe to use
echo
instead ofprintf
. And I read the problem description in this question as expressing a preference to print withecho
. The above function definition works in bash and ksh93. Althoughprintf
is more portable (and should usually be used for this sort of thing),echo
's syntax is arguably more readable.
Some shells'
echo
builtins interpret-
by itself as an option--even though the usual meaning of-
, to use stdin for input, is nonsensical forecho
. zsh does this. And there definitely existecho
s that don't recognize-n
, as it is not standard. (Many Bourne-style shells don't accept C-style for loops at all, thus theirecho
behavior needn't be considered..)
Here the task is to print the sequence; there, it was to assign it to a variable.
If $n
is the desired number of repetitions and you don't have to reuse it, and you want something even shorter:
while ((n--)); do echo -n "$s"; done; echo
n
must be a variable--this way doesn't work with positional parameters. $s
is the text to be repeated.
edited May 23 '17 at 12:34
Community♦
11
11
answered Sep 19 '14 at 14:23
Eliah KaganEliah Kagan
1,34721434
1,34721434
1
Strongly avoid doing loop versions.printf "%100s" | tr ' ' '='
is optimal.
– ocodo
Nov 4 '14 at 5:48
Good background info and kudos for packaging the functionality as a function, which works inzsh
as well, incidentally. The echo-in-a-loop approach works well for smaller repeat counts, but for larger ones there are POSIX-compliant alternatives based on utilities, as evidenced by @Slomojo's comment.
– mklement0
Apr 29 '15 at 15:53
Adding parentheses around your shorter loop preserves the value of n without affecting the echos:(while ((n--)); do echo -n "$s"; done; echo)
– user2350426
Aug 23 '15 at 4:24
add a comment |
1
Strongly avoid doing loop versions.printf "%100s" | tr ' ' '='
is optimal.
– ocodo
Nov 4 '14 at 5:48
Good background info and kudos for packaging the functionality as a function, which works inzsh
as well, incidentally. The echo-in-a-loop approach works well for smaller repeat counts, but for larger ones there are POSIX-compliant alternatives based on utilities, as evidenced by @Slomojo's comment.
– mklement0
Apr 29 '15 at 15:53
Adding parentheses around your shorter loop preserves the value of n without affecting the echos:(while ((n--)); do echo -n "$s"; done; echo)
– user2350426
Aug 23 '15 at 4:24
1
1
Strongly avoid doing loop versions.
printf "%100s" | tr ' ' '='
is optimal.– ocodo
Nov 4 '14 at 5:48
Strongly avoid doing loop versions.
printf "%100s" | tr ' ' '='
is optimal.– ocodo
Nov 4 '14 at 5:48
Good background info and kudos for packaging the functionality as a function, which works in
zsh
as well, incidentally. The echo-in-a-loop approach works well for smaller repeat counts, but for larger ones there are POSIX-compliant alternatives based on utilities, as evidenced by @Slomojo's comment.– mklement0
Apr 29 '15 at 15:53
Good background info and kudos for packaging the functionality as a function, which works in
zsh
as well, incidentally. The echo-in-a-loop approach works well for smaller repeat counts, but for larger ones there are POSIX-compliant alternatives based on utilities, as evidenced by @Slomojo's comment.– mklement0
Apr 29 '15 at 15:53
Adding parentheses around your shorter loop preserves the value of n without affecting the echos:
(while ((n--)); do echo -n "$s"; done; echo)
– user2350426
Aug 23 '15 at 4:24
Adding parentheses around your shorter loop preserves the value of n without affecting the echos:
(while ((n--)); do echo -n "$s"; done; echo)
– user2350426
Aug 23 '15 at 4:24
add a comment |
A pure Bash way with no eval
, no subshells, no external tools, no brace expansions (i.e., you can have the number to repeat in a variable):
If you're given a variable n
that expands to a (non-negative) number and a variable pattern
, e.g.,
$ n=5
$ pattern=hello
$ printf -v output '%*s' "$n"
$ output=${output// /$pattern}
$ echo "$output"
hellohellohellohellohello
You can make a function with this:
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}"
}
With this set:
$ repeat 5 hello output
$ echo "$output"
hellohellohellohellohello
For this little trick we're using printf
quite a lot with:
-v varname
: instead of printing to standard output,printf
will put the content of the formatted string in variablevarname
.- '%*s':
printf
will use the argument to print the corresponding number of spaces. E.g.,printf '%*s' 42
will print 42 spaces. - Finally, when we have the wanted number of spaces in our variable, we use a parameter expansion to replace all the spaces by our pattern:
${var// /$pattern}
will expand to the expansion ofvar
with all the spaces replaced by the expansion of$pattern
.
You can also get rid of the tmp
variable in the repeat
function by using indirect expansion:
repeat() {
# $1=number of patterns to repeat
# $2=pattern
# $3=output variable name
printf -v "$3" '%*s' "$1"
printf -v "$3" '%s' "${!3// /$2}"
}
Interesting variation to pass the variable name in. While this solution is fine for repeat counts up to around 1,000 (and thus probably fine for most real-life applications, if I were to guess), it gets very slow for higher counts (see next comment).
– mklement0
Apr 29 '15 at 19:19
It seems thatbash
's global string replacement operations in the context of parameter expansion (${var//old/new}
) are particularly slow: excruciatingly slow in bash3.2.57
, and slow in bash4.3.30
, at least on my OSX 10.10.3 system on a 3.2 Ghz Intel Core i5 machine: With a count of 1,000, things are slow (3.2.57
) / fast (4.3.30
): 0.1 / 0.004 seconds. Increasing the count to 10,000 yields strikingly different numbers:repeat 10000 = var
takes around 80 seconds(!) in bash3.2.57
, and around 0.3 seconds in bash4.3.30
(much faster than on3.2.57
, but still slow).
– mklement0
Apr 29 '15 at 19:19
add a comment |
A pure Bash way with no eval
, no subshells, no external tools, no brace expansions (i.e., you can have the number to repeat in a variable):
If you're given a variable n
that expands to a (non-negative) number and a variable pattern
, e.g.,
$ n=5
$ pattern=hello
$ printf -v output '%*s' "$n"
$ output=${output// /$pattern}
$ echo "$output"
hellohellohellohellohello
You can make a function with this:
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}"
}
With this set:
$ repeat 5 hello output
$ echo "$output"
hellohellohellohellohello
For this little trick we're using printf
quite a lot with:
-v varname
: instead of printing to standard output,printf
will put the content of the formatted string in variablevarname
.- '%*s':
printf
will use the argument to print the corresponding number of spaces. E.g.,printf '%*s' 42
will print 42 spaces. - Finally, when we have the wanted number of spaces in our variable, we use a parameter expansion to replace all the spaces by our pattern:
${var// /$pattern}
will expand to the expansion ofvar
with all the spaces replaced by the expansion of$pattern
.
You can also get rid of the tmp
variable in the repeat
function by using indirect expansion:
repeat() {
# $1=number of patterns to repeat
# $2=pattern
# $3=output variable name
printf -v "$3" '%*s' "$1"
printf -v "$3" '%s' "${!3// /$2}"
}
Interesting variation to pass the variable name in. While this solution is fine for repeat counts up to around 1,000 (and thus probably fine for most real-life applications, if I were to guess), it gets very slow for higher counts (see next comment).
– mklement0
Apr 29 '15 at 19:19
It seems thatbash
's global string replacement operations in the context of parameter expansion (${var//old/new}
) are particularly slow: excruciatingly slow in bash3.2.57
, and slow in bash4.3.30
, at least on my OSX 10.10.3 system on a 3.2 Ghz Intel Core i5 machine: With a count of 1,000, things are slow (3.2.57
) / fast (4.3.30
): 0.1 / 0.004 seconds. Increasing the count to 10,000 yields strikingly different numbers:repeat 10000 = var
takes around 80 seconds(!) in bash3.2.57
, and around 0.3 seconds in bash4.3.30
(much faster than on3.2.57
, but still slow).
– mklement0
Apr 29 '15 at 19:19
add a comment |
A pure Bash way with no eval
, no subshells, no external tools, no brace expansions (i.e., you can have the number to repeat in a variable):
If you're given a variable n
that expands to a (non-negative) number and a variable pattern
, e.g.,
$ n=5
$ pattern=hello
$ printf -v output '%*s' "$n"
$ output=${output// /$pattern}
$ echo "$output"
hellohellohellohellohello
You can make a function with this:
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}"
}
With this set:
$ repeat 5 hello output
$ echo "$output"
hellohellohellohellohello
For this little trick we're using printf
quite a lot with:
-v varname
: instead of printing to standard output,printf
will put the content of the formatted string in variablevarname
.- '%*s':
printf
will use the argument to print the corresponding number of spaces. E.g.,printf '%*s' 42
will print 42 spaces. - Finally, when we have the wanted number of spaces in our variable, we use a parameter expansion to replace all the spaces by our pattern:
${var// /$pattern}
will expand to the expansion ofvar
with all the spaces replaced by the expansion of$pattern
.
You can also get rid of the tmp
variable in the repeat
function by using indirect expansion:
repeat() {
# $1=number of patterns to repeat
# $2=pattern
# $3=output variable name
printf -v "$3" '%*s' "$1"
printf -v "$3" '%s' "${!3// /$2}"
}
A pure Bash way with no eval
, no subshells, no external tools, no brace expansions (i.e., you can have the number to repeat in a variable):
If you're given a variable n
that expands to a (non-negative) number and a variable pattern
, e.g.,
$ n=5
$ pattern=hello
$ printf -v output '%*s' "$n"
$ output=${output// /$pattern}
$ echo "$output"
hellohellohellohellohello
You can make a function with this:
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}"
}
With this set:
$ repeat 5 hello output
$ echo "$output"
hellohellohellohellohello
For this little trick we're using printf
quite a lot with:
-v varname
: instead of printing to standard output,printf
will put the content of the formatted string in variablevarname
.- '%*s':
printf
will use the argument to print the corresponding number of spaces. E.g.,printf '%*s' 42
will print 42 spaces. - Finally, when we have the wanted number of spaces in our variable, we use a parameter expansion to replace all the spaces by our pattern:
${var// /$pattern}
will expand to the expansion ofvar
with all the spaces replaced by the expansion of$pattern
.
You can also get rid of the tmp
variable in the repeat
function by using indirect expansion:
repeat() {
# $1=number of patterns to repeat
# $2=pattern
# $3=output variable name
printf -v "$3" '%*s' "$1"
printf -v "$3" '%s' "${!3// /$2}"
}
answered Jun 1 '14 at 9:15
gniourf_gniourfgniourf_gniourf
30.9k56683
30.9k56683
Interesting variation to pass the variable name in. While this solution is fine for repeat counts up to around 1,000 (and thus probably fine for most real-life applications, if I were to guess), it gets very slow for higher counts (see next comment).
– mklement0
Apr 29 '15 at 19:19
It seems thatbash
's global string replacement operations in the context of parameter expansion (${var//old/new}
) are particularly slow: excruciatingly slow in bash3.2.57
, and slow in bash4.3.30
, at least on my OSX 10.10.3 system on a 3.2 Ghz Intel Core i5 machine: With a count of 1,000, things are slow (3.2.57
) / fast (4.3.30
): 0.1 / 0.004 seconds. Increasing the count to 10,000 yields strikingly different numbers:repeat 10000 = var
takes around 80 seconds(!) in bash3.2.57
, and around 0.3 seconds in bash4.3.30
(much faster than on3.2.57
, but still slow).
– mklement0
Apr 29 '15 at 19:19
add a comment |
Interesting variation to pass the variable name in. While this solution is fine for repeat counts up to around 1,000 (and thus probably fine for most real-life applications, if I were to guess), it gets very slow for higher counts (see next comment).
– mklement0
Apr 29 '15 at 19:19
It seems thatbash
's global string replacement operations in the context of parameter expansion (${var//old/new}
) are particularly slow: excruciatingly slow in bash3.2.57
, and slow in bash4.3.30
, at least on my OSX 10.10.3 system on a 3.2 Ghz Intel Core i5 machine: With a count of 1,000, things are slow (3.2.57
) / fast (4.3.30
): 0.1 / 0.004 seconds. Increasing the count to 10,000 yields strikingly different numbers:repeat 10000 = var
takes around 80 seconds(!) in bash3.2.57
, and around 0.3 seconds in bash4.3.30
(much faster than on3.2.57
, but still slow).
– mklement0
Apr 29 '15 at 19:19
Interesting variation to pass the variable name in. While this solution is fine for repeat counts up to around 1,000 (and thus probably fine for most real-life applications, if I were to guess), it gets very slow for higher counts (see next comment).
– mklement0
Apr 29 '15 at 19:19
Interesting variation to pass the variable name in. While this solution is fine for repeat counts up to around 1,000 (and thus probably fine for most real-life applications, if I were to guess), it gets very slow for higher counts (see next comment).
– mklement0
Apr 29 '15 at 19:19
It seems that
bash
's global string replacement operations in the context of parameter expansion (${var//old/new}
) are particularly slow: excruciatingly slow in bash 3.2.57
, and slow in bash 4.3.30
, at least on my OSX 10.10.3 system on a 3.2 Ghz Intel Core i5 machine: With a count of 1,000, things are slow (3.2.57
) / fast (4.3.30
): 0.1 / 0.004 seconds. Increasing the count to 10,000 yields strikingly different numbers: repeat 10000 = var
takes around 80 seconds(!) in bash 3.2.57
, and around 0.3 seconds in bash 4.3.30
(much faster than on 3.2.57
, but still slow).– mklement0
Apr 29 '15 at 19:19
It seems that
bash
's global string replacement operations in the context of parameter expansion (${var//old/new}
) are particularly slow: excruciatingly slow in bash 3.2.57
, and slow in bash 4.3.30
, at least on my OSX 10.10.3 system on a 3.2 Ghz Intel Core i5 machine: With a count of 1,000, things are slow (3.2.57
) / fast (4.3.30
): 0.1 / 0.004 seconds. Increasing the count to 10,000 yields strikingly different numbers: repeat 10000 = var
takes around 80 seconds(!) in bash 3.2.57
, and around 0.3 seconds in bash 4.3.30
(much faster than on 3.2.57
, but still slow).– mklement0
Apr 29 '15 at 19:19
add a comment |
In bash 3.0 or higher
for i in {1..100};do echo -n =;done
add a comment |
In bash 3.0 or higher
for i in {1..100};do echo -n =;done
add a comment |
In bash 3.0 or higher
for i in {1..100};do echo -n =;done
In bash 3.0 or higher
for i in {1..100};do echo -n =;done
answered Mar 18 '11 at 8:49
adnansadnans
298211
298211
add a comment |
add a comment |
for i in {1..100}
do
echo -n '='
done
echo
add a comment |
for i in {1..100}
do
echo -n '='
done
echo
add a comment |
for i in {1..100}
do
echo -n '='
done
echo
for i in {1..100}
do
echo -n '='
done
echo
answered Mar 18 '11 at 8:50
Ignacio Vazquez-AbramsIgnacio Vazquez-Abrams
586k10610681169
586k10610681169
add a comment |
add a comment |
repeat() {
# $1=number of patterns to repeat
# $2=pattern
printf -v "TEMP" '%*s' "$1"
echo ${TEMP// /$2}
}
add a comment |
repeat() {
# $1=number of patterns to repeat
# $2=pattern
printf -v "TEMP" '%*s' "$1"
echo ${TEMP// /$2}
}
add a comment |
repeat() {
# $1=number of patterns to repeat
# $2=pattern
printf -v "TEMP" '%*s' "$1"
echo ${TEMP// /$2}
}
repeat() {
# $1=number of patterns to repeat
# $2=pattern
printf -v "TEMP" '%*s' "$1"
echo ${TEMP// /$2}
}
edited Oct 31 '14 at 15:44
ZoogieZork
10.2k54142
10.2k54142
answered Oct 31 '14 at 15:22
WSimpsonWSimpson
39436
39436
add a comment |
add a comment |
The question was about how to do it with echo
:
echo -e ''$_{1..100}'b='
This will will do exactly the same as perl -E 'say "=" x 100'
but with echo
only.
add a comment |
The question was about how to do it with echo
:
echo -e ''$_{1..100}'b='
This will will do exactly the same as perl -E 'say "=" x 100'
but with echo
only.
add a comment |
The question was about how to do it with echo
:
echo -e ''$_{1..100}'b='
This will will do exactly the same as perl -E 'say "=" x 100'
but with echo
only.
The question was about how to do it with echo
:
echo -e ''$_{1..100}'b='
This will will do exactly the same as perl -E 'say "=" x 100'
but with echo
only.
edited Jan 5 at 12:49
answered Jan 5 at 12:42
chevallierchevallier
3332519
3332519
add a comment |
add a comment |
In case that you want to repeat a character n times being n a VARIABLE number of times depending on, say, the length of a string you can do:
#!/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)
It displays:
vari equals.............................: AB
Up to 10 positions I must fill with.....: 8 equal signs
AB========
length
won't work withexpr
, you probably meantn=$(expr 10 - ${#vari})
; however, it's simpler and more efficient to use Bash's arithmetic expansion:n=$(( 10 - ${#vari} ))
. Also, at the core of your answer is the very Perl approach that the OP is looking for a Bash alternative to.
– mklement0
Aug 7 '15 at 12:55
add a comment |
In case that you want to repeat a character n times being n a VARIABLE number of times depending on, say, the length of a string you can do:
#!/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)
It displays:
vari equals.............................: AB
Up to 10 positions I must fill with.....: 8 equal signs
AB========
length
won't work withexpr
, you probably meantn=$(expr 10 - ${#vari})
; however, it's simpler and more efficient to use Bash's arithmetic expansion:n=$(( 10 - ${#vari} ))
. Also, at the core of your answer is the very Perl approach that the OP is looking for a Bash alternative to.
– mklement0
Aug 7 '15 at 12:55
add a comment |
In case that you want to repeat a character n times being n a VARIABLE number of times depending on, say, the length of a string you can do:
#!/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)
It displays:
vari equals.............................: AB
Up to 10 positions I must fill with.....: 8 equal signs
AB========
In case that you want to repeat a character n times being n a VARIABLE number of times depending on, say, the length of a string you can do:
#!/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)
It displays:
vari equals.............................: AB
Up to 10 positions I must fill with.....: 8 equal signs
AB========
edited Jun 15 '14 at 3:17
Camilo Martin
19.3k1692140
19.3k1692140
answered May 30 '13 at 14:14
Raul BaronRaul Baron
415
415
length
won't work withexpr
, you probably meantn=$(expr 10 - ${#vari})
; however, it's simpler and more efficient to use Bash's arithmetic expansion:n=$(( 10 - ${#vari} ))
. Also, at the core of your answer is the very Perl approach that the OP is looking for a Bash alternative to.
– mklement0
Aug 7 '15 at 12:55
add a comment |
length
won't work withexpr
, you probably meantn=$(expr 10 - ${#vari})
; however, it's simpler and more efficient to use Bash's arithmetic expansion:n=$(( 10 - ${#vari} ))
. Also, at the core of your answer is the very Perl approach that the OP is looking for a Bash alternative to.
– mklement0
Aug 7 '15 at 12:55
length
won't work with expr
, you probably meant n=$(expr 10 - ${#vari})
; however, it's simpler and more efficient to use Bash's arithmetic expansion: n=$(( 10 - ${#vari} ))
. Also, at the core of your answer is the very Perl approach that the OP is looking for a Bash alternative to.– mklement0
Aug 7 '15 at 12:55
length
won't work with expr
, you probably meant n=$(expr 10 - ${#vari})
; however, it's simpler and more efficient to use Bash's arithmetic expansion: n=$(( 10 - ${#vari} ))
. Also, at the core of your answer is the very Perl approach that the OP is looking for a Bash alternative to.– mklement0
Aug 7 '15 at 12:55
add a comment |
This is the longer version of what Eliah Kagan was espousing:
while [ $(( i-- )) -gt 0 ]; do echo -n " "; done
Of course you can use printf for that as well, but not really to my liking:
printf "%$(( i*2 ))s"
This version is Dash compatible:
until [ $(( i=i-1 )) -lt 0 ]; do echo -n " "; done
with i being the initial number.
In bash and with a positive n:while (( i-- )); do echo -n " "; done
works.
– user2350426
Aug 23 '15 at 4:27
add a comment |
This is the longer version of what Eliah Kagan was espousing:
while [ $(( i-- )) -gt 0 ]; do echo -n " "; done
Of course you can use printf for that as well, but not really to my liking:
printf "%$(( i*2 ))s"
This version is Dash compatible:
until [ $(( i=i-1 )) -lt 0 ]; do echo -n " "; done
with i being the initial number.
In bash and with a positive n:while (( i-- )); do echo -n " "; done
works.
– user2350426
Aug 23 '15 at 4:27
add a comment |
This is the longer version of what Eliah Kagan was espousing:
while [ $(( i-- )) -gt 0 ]; do echo -n " "; done
Of course you can use printf for that as well, but not really to my liking:
printf "%$(( i*2 ))s"
This version is Dash compatible:
until [ $(( i=i-1 )) -lt 0 ]; do echo -n " "; done
with i being the initial number.
This is the longer version of what Eliah Kagan was espousing:
while [ $(( i-- )) -gt 0 ]; do echo -n " "; done
Of course you can use printf for that as well, but not really to my liking:
printf "%$(( i*2 ))s"
This version is Dash compatible:
until [ $(( i=i-1 )) -lt 0 ]; do echo -n " "; done
with i being the initial number.
answered Apr 2 '15 at 23:33
Xennex81Xennex81
13415
13415
In bash and with a positive n:while (( i-- )); do echo -n " "; done
works.
– user2350426
Aug 23 '15 at 4:27
add a comment |
In bash and with a positive n:while (( i-- )); do echo -n " "; done
works.
– user2350426
Aug 23 '15 at 4:27
In bash and with a positive n:
while (( i-- )); do echo -n " "; done
works.– user2350426
Aug 23 '15 at 4:27
In bash and with a positive n:
while (( i-- )); do echo -n " "; done
works.– user2350426
Aug 23 '15 at 4:27
add a comment |
Python is ubiquitous and works the same everywhere.
python -c "import sys; print('*' * int(sys.argv[1]))" "=" 100
Character and count are passed as separate parameters.
add a comment |
Python is ubiquitous and works the same everywhere.
python -c "import sys; print('*' * int(sys.argv[1]))" "=" 100
Character and count are passed as separate parameters.
add a comment |
Python is ubiquitous and works the same everywhere.
python -c "import sys; print('*' * int(sys.argv[1]))" "=" 100
Character and count are passed as separate parameters.
Python is ubiquitous and works the same everywhere.
python -c "import sys; print('*' * int(sys.argv[1]))" "=" 100
Character and count are passed as separate parameters.
answered Mar 1 '18 at 0:16
loevborgloevborg
1,368916
1,368916
add a comment |
add a comment |
How could I do this with echo?
You can do this with echo
if the echo
is followed by sed
:
echo | sed -r ':a s/^(.*)$/=1/; /^={100}$/q; ba'
Actually, that echo
is unnecessary there.
add a comment |
How could I do this with echo?
You can do this with echo
if the echo
is followed by sed
:
echo | sed -r ':a s/^(.*)$/=1/; /^={100}$/q; ba'
Actually, that echo
is unnecessary there.
add a comment |
How could I do this with echo?
You can do this with echo
if the echo
is followed by sed
:
echo | sed -r ':a s/^(.*)$/=1/; /^={100}$/q; ba'
Actually, that echo
is unnecessary there.
How could I do this with echo?
You can do this with echo
if the echo
is followed by sed
:
echo | sed -r ':a s/^(.*)$/=1/; /^={100}$/q; ba'
Actually, that echo
is unnecessary there.
answered Feb 1 at 13:12
DaBlerDaBler
1,26221630
1,26221630
add a comment |
add a comment |
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
}
Sample runs
$ repeatString 'a1' 10
a1a1a1a1a1a1a1a1a1a1
$ repeatString 'a1' 0
$ repeatString '' 10
Reference lib at: https://github.com/gdbtek/linux-cookbooks/blob/master/libraries/util.bash
add a comment |
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
}
Sample runs
$ repeatString 'a1' 10
a1a1a1a1a1a1a1a1a1a1
$ repeatString 'a1' 0
$ repeatString '' 10
Reference lib at: https://github.com/gdbtek/linux-cookbooks/blob/master/libraries/util.bash
add a comment |
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
}
Sample runs
$ repeatString 'a1' 10
a1a1a1a1a1a1a1a1a1a1
$ repeatString 'a1' 0
$ repeatString '' 10
Reference lib at: https://github.com/gdbtek/linux-cookbooks/blob/master/libraries/util.bash
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
}
Sample runs
$ repeatString 'a1' 10
a1a1a1a1a1a1a1a1a1a1
$ repeatString 'a1' 0
$ repeatString '' 10
Reference lib at: https://github.com/gdbtek/linux-cookbooks/blob/master/libraries/util.bash
answered Mar 8 '18 at 18:30
Nam NguyenNam Nguyen
2,67083663
2,67083663
add a comment |
add a comment |
Simplest is to use this one-liner in csh/tcsh:
printf "%50sn" '' | tr '[:blank:]' '[=]'
add a comment |
Simplest is to use this one-liner in csh/tcsh:
printf "%50sn" '' | tr '[:blank:]' '[=]'
add a comment |
Simplest is to use this one-liner in csh/tcsh:
printf "%50sn" '' | tr '[:blank:]' '[=]'
Simplest is to use this one-liner in csh/tcsh:
printf "%50sn" '' | tr '[:blank:]' '[=]'
edited Jan 1 at 6:54
double-beep
2,69241028
2,69241028
answered Dec 31 '18 at 17:51


Shawn GivlerShawn Givler
1
1
add a comment |
add a comment |
My answer is a bit more complicated, and probably not perfect, but for those looking to output large numbers, I was able to do around 10 million in 3 seconds.
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}
}
add a comment |
My answer is a bit more complicated, and probably not perfect, but for those looking to output large numbers, I was able to do around 10 million in 3 seconds.
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}
}
add a comment |
My answer is a bit more complicated, and probably not perfect, but for those looking to output large numbers, I was able to do around 10 million in 3 seconds.
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}
}
My answer is a bit more complicated, and probably not perfect, but for those looking to output large numbers, I was able to do around 10 million in 3 seconds.
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}
}
answered Feb 23 at 7:50
Silver OgreSilver Ogre
1
1
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f5349718%2fhow-can-i-repeat-a-character-in-bash%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
possible duplicate of shell script create string of repeated characters
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Apr 10 '14 at 10:54
Sadly this is not Bash.
– solidsnack
Feb 20 '16 at 20:42
1
not with echo, but on the same topic
ruby -e 'puts "=" * 100'
orpython -c 'print "=" * 100'
– Evgeny
Apr 20 '17 at 15:53
1
Great question. Very good answers. I've used one of the answers in a real job here, that I'll post as an example: github.com/drbeco/oldfiles/blob/master/oldfiles (used
printf
withseq
)svrb=`printf '%.sv' $(seq $vrb)`
– Dr Beco
Jul 7 '17 at 5:21