113

Is there any built-in Linux command that allows to output a string that is n times an input string??

Zombo
  • 1
  • 24
  • 120
  • 163
GetFree
  • 3,040
  • 10
  • 40
  • 47
  • 2
    By "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 Petrescu Dec 22 '09 at 03:12
  • 9
    I tagged the question with "bash". I thought that would've been enough. – GetFree Dec 22 '09 at 03:27
  • 2
    Related on [SO]: http://stackoverflow.com/q/3211891/2157640 – Palec Nov 16 '14 at 14:33

17 Answers17

126
adrian@Fourier:~$ printf 'HelloWorld\n%.0s' {1..5}
HelloWorld
HelloWorld
HelloWorld
HelloWorld
HelloWorld
adrian@Fourier:~$
Adrian Petrescu
  • 3,318
  • 2
  • 24
  • 24
  • 12
    Could 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. – GetFree Dec 22 '09 at 03:24
  • 9
    It'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! – Adrian Petrescu Dec 22 '09 at 03:44
  • I see. It's the particular behavior of printf what allows the repetition – GetFree Dec 22 '09 at 03: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 :) – Adrian Petrescu Dec 22 '09 at 03:53
  • NB: The expansion of {x..y} is not specific to printf. This is a bash syntax that expands to a sequence of numbers x, x+1, ..., y. For example, try "echo {1..10}". This is in the "Brace Expansion" section of the bash man page (and is called a "sequence expression"). – larsks Dec 22 '09 at 14:25
  • 4
    What *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. – Dennis Williamson May 08 '12 at 20:38
  • Uggh. Why `bash` (POSIX?) chose to do this is totally beyond me. I've been bitten by this when messing with my GRUB2 configuration. – new123456 May 08 '12 at 21:20
  • This doesn't work if we want to repeat 0 time. – icando Jun 16 '12 at 04:51
  • I love this but it doesn't work if the string to be repeated is exactly `-`, which just so happens to be what I want (making a little banner with hyphens). Oh well. I'll upgrade the two banner types from `---`/`===` to `===`/`###`. Oh, another wrinkle, best to quote shellcheck: `Bash doesn't support variables in brace range expansions.` – Steven Lu May 13 '22 at 04:41
  • @StevenLu this will work for you: `printf -- '-%.0s' {1..5}` and for variable length: `printf -- '-%.0s' $(seq 1 $length)`, but the command substitution is relatively expensive, performance wise ;). – MichaIng May 02 '23 at 13:03
  • Nice! good trick there thanks. Haha I can't believe I didn't think of using positional arguments – Steven Lu May 02 '23 at 19:08
107

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}
Dennis Williamson
  • 106,229
  • 19
  • 167
  • 187
  • How do you get rid of the new lines when using the yes command? – GetFree Dec 22 '09 at 04:16
  • 4
    One way is to pipe it through `sed -n 'H;${x;s/\n//gp}'` or `sed -n ':t;${s/\n//gp};N;bt'` another is to do `echo $(yes "HelloWorld" | head -n 10)` which adds a space between each copy of the string. Yet another way is to pipe it through `tr -d '\n'` which also eliminates the final newline. – Dennis Williamson Dec 22 '09 at 09:08
  • 2
    It's like Haskell in a bash shell: Hashell? – wchargin Jan 15 '14 at 05:20
  • 6
    The yes solution is pretty neat – piggybox Jun 26 '15 at 20:04
  • The `yes` solution was exactly what I needed, since I wanted to duplicate a command (whose output fluctuates). Thus: `yes "$(my-command)" | head -n 2` – arnsholt Aug 02 '16 at 14:47
  • 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, compare `yes "$(date +%N; sleep 1)" | head -n 4` and `for i in {1..4}; do date +%N; sleep 1; done` – Dennis Williamson Aug 02 '16 at 22:35
  • The output is based on a random sampling, so running the command twice yields somewhat different results each time. I actually wanted the exact same string twice (because the output was going into a script that generates a table, and that particular row was supposed to be the same number twice [where other rows varied, obviously]). – arnsholt Aug 03 '16 at 19:02
  • 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. – Dennis Williamson Aug 03 '16 at 19:15
25

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.

Dave
  • 25,297
  • 10
  • 57
  • 69
Wojtek Waga
  • 359
  • 3
  • 3
  • 3
    It's an ugly ugly hack, and yet I can't help but love it and use it. – 16807 Jul 14 '17 at 17:38
  • 8
    Love it! Maybe using `$_` instead of `$wojek` makes the intent clearer. – Mihai Todor Sep 04 '19 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` assuming `set -eu` is set above. – macieksk Apr 24 '21 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 the `echo` technique. You can also do `varname=$(echo word$_{1..10})`. – Dennis Williamson Nov 27 '22 at 21:32
  • Try `_5=MIDPOINT` before you do `echo word$_{1..10}`. To make sure your temp variables are unset so something like that doesn't happen: `unset _{1..10}` – Dennis Williamson Nov 27 '22 at 22:10
  • Now THIS is exactly the kind of dirty hack that Just Works that I was looking for! – J.M. Janzen Jun 15 '23 at 20:38
18

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}
twifkak
  • 189
  • 1
  • 2
  • 3
    This is the shortest [POSIX 7](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html#tag_20_94) solution I have seen so far since `seq`, `yes` and `{1..5}` are not POSIX 7. – Ciro Santilli OurBigBook.com Apr 10 '14 at 11:27
  • 5
    You 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. – Dennis Williamson Jan 05 '19 at 14:22
17

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
John T
  • 163,373
  • 27
  • 341
  • 348
12

Repeat n times, just put n-1 commas between {}:

$ echo 'helloworld'{,,}
helloworld helloworld helloworld

Repeats 'helloworld' twice after the first echo.

TankorSmash
  • 284
  • 2
  • 13
kev
  • 12,462
  • 13
  • 59
  • 72
11

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 :)

Adrian Petrescu
  • 3,318
  • 2
  • 24
  • 24
  • This gets my vote, as it's entirely shell-internal; no forking required. – esm Dec 23 '09 at 18:08
  • 2
    What 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. – CodeGnome Apr 17 '12 at 20:17
  • This is the only one so far (`yes`, `printf`, `for i in {1..5}`) that if the `n` 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 `<`) – Mehrad Mahmoudian Jun 19 '19 at 08:54
9

No magic here:

seq 5 | awk '{print "Hello World"}'
phuclv
  • 26,555
  • 15
  • 113
  • 235
brablc
  • 1,460
  • 1
  • 11
  • 8
8

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
n.caillou
  • 180
  • 1
  • 4
6

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";
Zombo
  • 1
  • 24
  • 120
  • 163
5

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!

nickl-
  • 500
  • 5
  • 6
4

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
  • 4
    In `seq (GNU coreutils) 8.25`, this gives `seq: 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. – Palec Jun 19 '17 at 10:08
  • Doesn't work on macOS – Adam Stewart Apr 28 '23 at 21:20
3

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.

glenn jackman
  • 25,463
  • 6
  • 46
  • 69
3

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'
eqzx
  • 2,458
  • 7
  • 29
  • 42
  • 4
    Not 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. – Adrian Petrescu Dec 22 '09 at 03:18
  • 9
    I 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' – larsks Dec 22 '09 at 14:26
  • 1
    I like the readability of this solution, good reason enough for me. ;) – elias Aug 16 '15 at 20:28
  • @AdrianPetrescu this is hardly different from the other solutions that are using things like `sed` and `awk` 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-tier May 15 '22 at 00:39
2
line="==========================="
line=${line:0:10}
${line//"="/"ten "}

outputs

ten ten ten ten ten ten ten ten ten ten
commonpike
  • 343
  • 3
  • 11
1

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}`, whereas `echo {1..3}` produces `1 2 3`. – Gordon Bean Oct 02 '20 at 22:37
0

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
phuclv
  • 26,555
  • 15
  • 113
  • 235