Is there any built-in Linux command that allows to output a string that is n times an input string??
-
2By "built-in linux command" I assume you mean shell command, and since you don't mention which shell you're using, I assume it's bash. You can check this by typing "echo $SHELL" at the command line and you should get something similar to "/bin/bash" back. If you don't, you should edit your answer to specify what it does show. Cheers :)– Adrian PetrescuCommented Dec 22, 2009 at 3:12
-
9I tagged the question with "bash". I thought that would've been enough.– GetFreeCommented Dec 22, 2009 at 3:27
-
2Related on Stack Overflow: stackoverflow.com/q/3211891/2157640– PalecCommented Nov 16, 2014 at 14:33
17 Answers
adrian@Fourier:~$ printf 'HelloWorld\n%.0s' {1..5}
HelloWorld
HelloWorld
HelloWorld
HelloWorld
HelloWorld
adrian@Fourier:~$
-
12Could you explain how this works? I understand the printf command since it's the same as the one in C/C++. But I dont understand how the {1..5} is expanded and how that works in conjunction with the "%.0s" part.– GetFreeCommented Dec 22, 2009 at 3:24
-
9It's a bit of a hack :) Try running "printf 'HelloWorld %d\n' 1 2 3 4 5" and it'll probably click for you. The %.0s flag is meant to do nothing, just be there to pick up arguments. Then, if bash gets more arguments than it has format specifiers, it will simply print out multiple copies, grabbing as many as it needs. So you get the effect. The output of "printf 'HelloWorld %d%d\n' 1 2 3 4 5 6" probably makes it even clearer. Hope this helps! Commented Dec 22, 2009 at 3:44
-
I see. It's the particular behavior of printf what allows the repetition– GetFreeCommented Dec 22, 2009 at 3:53
-
1(I should note that, from a purely theoretical standpoint, this is probably the fastest solution since it uses a single shell primitive -- not any external processes. In reality though, let's face it, performance of bash scripts doesn't really matter :) Commented Dec 22, 2009 at 3:53
-
4What is specific to
printf
is that it will repeatedly apply excess arguments to the format string, "looping" for free. By "excess", I mean that there are more arguments than%
placeholders. Commented May 8, 2012 at 20:38
Here's an old-fashioned way that's pretty portable:
yes "HelloWorld" | head -n 10
This is a more conventional version of Adrian Petrescu's answer using brace expansion:
for i in {1..5}
do
echo "HelloWorld"
done
That's equivalent to:
for i in 1 2 3 4 5
This is a little more concise and dynamic version of pike's answer:
printf -v spaces '%*s' 10 ''; printf '%s\n' ${spaces// /ten}
-
4One way is to pipe it through
sed -n 'H;${x;s/\n//gp}'
orsed -n ':t;${s/\n//gp};N;bt'
another is to doecho $(yes "HelloWorld" | head -n 10)
which adds a space between each copy of the string. Yet another way is to pipe it throughtr -d '\n'
which also eliminates the final newline. Commented Dec 22, 2009 at 9:08 -
2
-
6
-
1@arnsholt: I'm not sure what you mean by fluctuate. The command substitution only gets run once and
yes
outputs the same thing (the single output of the command substitution) multiple times. For example, compareyes "$(date +%N; sleep 1)" | head -n 4
andfor i in {1..4}; do date +%N; sleep 1; done
Commented Aug 2, 2016 at 22:35 -
1@arnsholt: OK, I would use a
for
loop instead of spawning an external executable (yes
). Or capture it once and output it twice like:out=$(my-command); printf '%s\n' "$out" "$out"
(printf
will apply excess arguments sequentially) or other similar techniques. Commented Aug 3, 2016 at 19:15
You can use a trick. Echoing an empty variable does not print anything. So you can write:
echo word$wojek{1..100}
If $wojek1 $wojek2
... $wojek100
are non-existing variables you will get your word repeated 100 times without anything else.
-
4It's an ugly ugly hack, and yet I can't help but love it and use it.– 16807Commented Jul 14, 2017 at 17:38
-
8Love it! Maybe using
$_
instead of$wojek
makes the intent clearer. Commented Sep 4, 2019 at 14:13 -
Great trick. For those who care a little bit more about bash scripts' correctness this should be:
set +u; echo word$wojek{1..100}; set -u
assumingset -eu
is set above.– maciekskCommented Apr 24, 2021 at 12:33 -
If you want to use this technique to place the repeated string in a variable:
printf -v varname '%s' word$_{1..10}
. You can include any formatting you like. One difference is that if you use'%s '
(ends in a space) there will be a trailing space which isn't present in theecho
technique. You can also dovarname=$(echo word$_{1..10})
. Commented Nov 27, 2022 at 21:32 -
Try
_5=MIDPOINT
before you doecho word$_{1..10}
. To make sure your temp variables are unset so something like that doesn't happen:unset _{1..10}
Commented Nov 27, 2022 at 22:10
This can be parameterized and doesn't require a temp variable, FWIW:
printf "%${N}s" | sed 's/ /blah/g'
Or, if $N
is the size of a bash array:
echo ${ARR[@]/*/blah}
-
3This is the shortest POSIX 7 solution I have seen so far since
seq
,yes
and{1..5}
are not POSIX 7. Commented Apr 10, 2014 at 11:27 -
5You shouldn't mix data into
printf
's format specifier. What if the data contains format strings? This is the correct way to dynamically specify the "precision" (length):printf '%*s' "$N"
- make it a habit to enclose the format string in single quotes to prevent variable expansion there. Commented Jan 5, 2019 at 14:22
Quite a few good ways already mentioned. Can't forget about good old seq
though:
[john@awesome]$for i in `seq 5`; do echo "Hi";done Hi Hi Hi Hi Hi
-
This one actually respects a zero, treating it as zero! (The
{i..j}
trick never returns an empty range.)– JellicleCommented Oct 15, 2016 at 2:48
Perhaps another way that is more general and useful for you:
adrian@Fourier:~$ n=5
adrian@Fourier:~$ for (( c=1; c<=n; c++)) ; do echo "HelloWorld" ; done
HelloWorld
HelloWorld
HelloWorld
HelloWorld
HelloWorld
adrian@Fourier:~$
The bash shell is more powerful than most people think :)
-
This gets my vote, as it's entirely shell-internal; no forking required.– esmCommented Dec 23, 2009 at 18:08
-
2What forking are you referring to? The original answer requires none. The output of 'type printf' is 'printf is a shell builtin' and therefore runs within the original bash process. Commented Apr 17, 2012 at 20:17
-
This is the only one so far (
yes
,printf
,for i in {1..5}
) that if then
is zero, it returns empty string without exist status of 1. Also due to the mathematical notation for comparison, it is easy to have offset by 1 (e.g by changing the<=
to<
)– MehradCommented Jun 19, 2019 at 8:54
Repeat n
times, just put n-1
commas between {}
:
$ echo 'helloworld'{,,}
helloworld helloworld helloworld
Repeats 'helloworld' twice after the first echo.
-
7
-
6
-
this doesn't seem to let you put newlines in the string either (ie getting helloworld on their own lines) Commented Feb 1, 2019 at 16:54
-
@GetFree: most solutions here use bash's
{1..5}
expansion, which also does not support parametrize from a variable Commented Nov 22, 2021 at 11:10
No magic here:
seq 5 | awk '{print "Hello World"}'
-
If whitespace between the lines is not desired, can change it to printf:
seq 5 | awk '{printf "Hello World"}'
Commented Jan 18, 2023 at 14:50
I've experienced broken pipe warnings with the yes
solution, so here's another good alternative:
$ seq 4 | sed -E 's/.+/foo/'
foo
foo
foo
foo
-
-
These days I'd probably go with
sed -E 's/.+/foo/'
instead :) less arcane and more compatible;sed -E
is historically supported by bsd and gnu, and is becoming posix Commented Sep 11, 2022 at 8:43 -
-
@AdamStewart Indeed, bsd sed's
c
command works a bit differently; the solution was given in the previous command though. anyway, i've updated the answer Commented May 5, 2023 at 10:11
based on what @pike was hinting at
for every character in string echo string
echo ${target//?/$replace}
An example of a heading underlined with =
characters
export heading='ABCDEF';
export replace='=';
echo -e "${heading}\n${heading//?/$replace}"
will output
ABCDEF
======
This seems to port between linux and OS X and that makes me happy.
nJoy!
POSIX AWK:
#!/usr/bin/awk -f
function str_repeat(s1, n1) {
s2 = ""
for (n2 = 1; n2 <= n1; n2++) {
s2 = s2 s1
}
return s2
}
BEGIN {
s3 = str_repeat("Sun", 5)
print s3
}
Or PHP:
<?php
$s3 = str_repeat('Sun', 5);
echo $s3, "\n";
-
Use this without the awk: while (( c++ < 5 )); do printf 'hello'; done– emfCommented Feb 21, 2017 at 8:33
-
If you're on BSD, you can just use seq
.
$ seq -f "Hello, world" 5
Hello, world
Hello, world
Hello, world
Hello, world
Hello, world
-
4In
seq (GNU coreutils) 8.25
, this givesseq: format 'Hello, world' has no % directive
, forcing a formatting directive to be present. GNU coreutils are used e.g. n many Linux distributions and Cygwin. Including the info here for those missing the info that it works only in BSD seq.– PalecCommented Jun 19, 2017 at 10:08 -
Assuming you want something like Perl's x
operator, where you don't automatically get a newline between repetitions:
x() {
# usage: x string num
for i in $(seq 1 $2); do printf "%s" "$1"; done
# print a newline only if the string does not end in a newline
[[ "$1" == "${1%$'\n'}" ]] && echo ""
}
x Hi 10 # ==> HiHiHiHiHiHiHiHiHiHi
x $'Hello World!\n' 3
I explicitly used a for
loop because you can't write {1..$n}
in bash: brace expansion is done before variable substitution.
Not exactly built in to linux, but if you have python installed..
python
>>>var = "string"
>>>var*n
Or in one line, as commenter suggested:
python -c 'print "This is a test.\n" * 10'
-
4Not really that useful, since it can't be easily integrated into shell scripts, etc. And since there's about a billion ways to do this in the shell itself, I see little reason to bring out the big guns (i.e Python) for it. Commented Dec 22, 2009 at 3:18
-
9I agree with your second point but not the fist...it's easy to integrate into a shell script: python -c 'print "This is a test.\n" * 10'– larsksCommented Dec 22, 2009 at 14:26
-
1I like the readability of this solution, good reason enough for me. ;)– eliasCommented Aug 16, 2015 at 20:28
-
@AdrianPetrescu this is hardly different from the other solutions that are using things like
sed
andawk
except that it's more readable. Unless you're running it in a tight loop many times, the cost of starting the interpreter is negligible.– Z4-tierCommented May 15, 2022 at 0:39
line="==========================="
line=${line:0:10}
${line//"="/"ten "}
outputs
ten ten ten ten ten ten ten ten ten ten
-
1Maybe a more verbose example: declare c='-----'; c=${c//${c:0:1}/$c}; echo $c # Prints "-" 25 times. Commented Jan 2, 2013 at 23:02
On macOS and BSD you can use jot
# Print 5 lines of `HelloWorld`
jot -b "HelloWorld" 5
# Print `HelloWorld` 5 times without newlines
jot -s "" -b "HelloWorld" 5
# Print `Hello` 5 times using a different separator which is `World` instead of newline
jot -s "World" -b "Hello" 5
On Linux you can install athena-jot to get it
Like yes
, this solution allows for any number of repetition $N
and is very fast although not extremely fast like yes
or customized native solutions with SIMD and splice which can achieve tens of GBs of data per second
With yes
you can print without new lines like this
$ N=10000000
$ WORD=HelloWorld
$ yes $WORD | tr -d '\n' | head -c $(($N * ${#WORD}))
Benchmark:
Looping directly with the shell is slow for obvious reasons, therefore will be unsuitable for huge $N
values. You'll always need some external tools like yes
, sed
, tr
... for fast output. The brace expansion trick {1..$N}
also completely fails when $N
is large due to argument list too long error and using variable string replacement like in commonpike's answer will make the shell give up soon due to lack of memory or the OOM will kick in
Below is some results when printing the word following by a new line
$ time bash -c "echo $WORD\$_{1..$N} >/dev/null"
real 0m15.863s
user 0m15.189s
sys 0m0.646s
$ time bash -c "for i in {1..$N}; do echo $WORD; done >/dev/null"
real 0m42.728s
user 0m38.029s
sys 0m4.673s
$ time bash -c "for i in \$(seq $N); do echo $WORD; done >/dev/null"
real 0m43.073s
user 0m39.349s
sys 0m4.507s
As you can see they're very slow. External tools will blow them out
$ time bash -c "printf %${N}s | sed 's/ /$WORD\n/g' >/dev/null"
real 0m1.913s
user 0m1.847s
sys 0m0.071s
$ time jot -b $WORD $N >/dev/null
real 0m1.262s
user 0m1.255s
sys 0m0.005s
$ time yes $WORD | head -n $N >/dev/null
real 0m0.912s
user 0m0.903s
sys 0m0.031s
$ time gyes $WORD | head -n $N >/dev/null
real 0m0.930s
user 0m0.947s
sys 0m0.090s
$ time yes $WORD | ghead -n $N >/dev/null
real 0m0.246s
user 0m0.138s
sys 0m0.213s
$ time gyes $WORD | ghead -n $N >/dev/null
real 0m0.258s
user 0m0.175s
sys 0m0.242s
I'm doing these tests on macOS and in the last 4 tests you can see how terrible BSD tools are compared to GNU tools (prefixed with g
due to name clashing with the default BSD tools), even though they're still far faster than the shell
The above cases are with a trailing new line, and when printing without new lines GNU tools also leave BSD tools in the dust
$ time yes $WORD | tr -d '\n' | ghead -c 300M >/dev/null
real 0m26.763s
user 0m26.857s
sys 0m0.381s
$ time gyes $WORD | tr -d '\n' | ghead -c 300M >/dev/null
real 0m27.972s
user 0m28.142s
sys 0m0.598s
$ time yes $WORD | gtr -d '\n' | ghead -c 300M >/dev/null
real 0m1.353s
user 0m0.609s
sys 0m0.981s
$ time gyes $WORD | gtr -d '\n' | ghead -c 300M >/dev/null
real 0m0.980s
user 0m0.785s
sys 0m1.095s
Try this one:
echo $(for i in $(seq 1 100); do printf "-"; done)
Will create (a hundred dash):
-
I like this answer, as when "n" is an environment var (e.g.
$n
), the curly-brace syntax doesn't work:n=10 && echo {1..$n}
produces{1..10}
, whereasecho {1..3}
produces1 2 3
. Commented Oct 2, 2020 at 22:37