333

How could I do this with echo?

perl -E 'say "=" x 100'
Mateusz Piotrowski
  • 8,029
  • 10
  • 53
  • 79
sid_com
  • 24,137
  • 26
  • 96
  • 187
  • Sadly this is not Bash. – solidsnack Feb 20 '16 at 20:42
  • 2
    not with echo, but on the same topic `ruby -e 'puts "=" * 100'` or `python -c 'print "=" * 100'` – Evgeny Apr 20 '17 at 15:53
  • 4
    Great question. Very good answers. I've used one of the answers in a real job here, that I'll post as an example: https://github.com/drbeco/oldfiles/blob/master/oldfiles (used `printf` with `seq`) `svrb=\`printf '%.sv' $(seq $vrb)\`` – DrBeco Jul 07 '17 at 05:21
  • A generic solution to print whatever (1 or more characters, even including newlines): Repeat_this () { i=1; while [ "$i" -le "$2" ]; do printf "%s" "$1"; i=$(( $i + 1 )) ; done ; printf '\n' ;} . Use like this: Repeat_this "something" Number_of_repetitions. For example, to showcase repeating 5 times something including 3 newlines: Repeat_this "$(printf '\n\n\nthis')" 5 . The final printf '\n' may be taken out (but I put it in to create text files, and those need a newline as their last character!) – Olivier Dulac Feb 14 '20 at 10:09
  • Using `Perl` is already good enough for me. Tried several answers but they all have a `%` at the end of the string, don't know why. – Deqing Jun 16 '22 at 23:44
  • Ok, just found out why, it should be `Perl -E 'print "=" x 100'` – Deqing Jun 20 '22 at 06:43

37 Answers37

538

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.

Manuel Jordan
  • 15,253
  • 21
  • 95
  • 158
dogbane
  • 266,786
  • 75
  • 396
  • 414
  • 20
    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 07 '13 at 21:34
  • 9
    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
  • 87
    You can't use variables within brace expansion. Use `seq` instead e.g. `$(seq 1 $limit)`. – dogbane Jan 11 '14 at 08:22
  • 1
    Here's another function that uses a word as the argument and prints based on the number of chars in the word: `repl () { printf '=%.0s' $(seq 2 $(echo $1 | wc -c)); }` Usage: `repl 'Installing something'` or `repl "Installing $package"` – reubano May 01 '14 at 08:07
  • 1
    You can use variables with brace expansion in `ksh` and `zsh`, unfortunately it's just `bash` which expands things in a different and silly order. – Adrian Frühwirth May 02 '14 at 12:43
  • 18
    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 07:35
  • @dogbane `n=5; eval "echo {1..$n}"` But seq is more elegant. – Palec Nov 16 '14 at 14:18
  • 8
    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 08 '15 at 10:25
  • 3
    If you want to specify the width using a variable `w`, how about `printf "%${w}s" '' | tr ' ' =`? – IpsRich Jul 24 '15 at 11:56
  • You also can use: `len=20; eval "printf "%0.s$c" {1..$len}"` – rowan Mar 08 '17 at 04:55
  • 2
    it also works without zero following the dot: `echo "$(printf '=%.s' {1..100})"` – Alex Jun 12 '17 at 12:19
  • +1 on answer but I still went with `Equals="============="` and `Dashes="------------"` defined in bash global section and then referenced in printing loops within local bash functions for performance and readability. Feeling kind of guilty today but I know I'll appreciate the KISS system 5 years from now. – WinEunuuchs2Unix Jun 24 '17 at 23:50
  • 1
    If trying to repeat hyphens `-`, you can put them at the end of the format string `'%.0s-'`, otherwise you will get an error because the leading hyphen will be interpreted as an option. – wisbucky Dec 09 '17 at 00:25
  • To add a newline you can do: echo $(printf '=%.0s' {1..100}) – AmanicA Jun 07 '18 at 09:56
  • `printf '=%.0s'` outputs `=`. So, not an option when you need arbitrary number of `=`'s. Or to be precise, arbitrary number of spaces to make an indent. The [other](https://stackoverflow.com/a/5349796/52499) solution accommodates this case just fine. Namely, `printf "%${n}s" | tr ' ' '='` – x-yuri Sep 18 '18 at 10:03
  • what is meaning behind `.0` mean in this answer? And is this regex or `printf` –  Mar 16 '19 at 16:47
  • @0x476f72616e From `man 3 printf`: "An optional precision, in the form of a period ('.') followed by an optional decimal digit string. [..] If the precision is given as just '.', the precision is taken to be zero. [..] the maximum number of characters to be printed from a string for s and S conversions." Therefore, I'd expect it to work without the `0` too. – Jonathan Komar Sep 21 '20 at 08:19
  • If you do this in vim, you'll need to escape the `%`. Try adding a comment line in LaTeX: `read !printf '\%\%\%.s' {1..50}` – Jonathan Komar Sep 21 '20 at 08:28
  • Simply as ```echo {1..100}``` – tedyyu Sep 24 '21 at 04:32
  • Z shell: use `PROMPT_EOL_MARK=''` to [hide trailing percent symbol at EOL](https://unix.stackexchange.com/a/167600). – Faxopita Nov 30 '22 at 17:36
127

No easy way. But for example:

seq -s= 100|tr -d '[:digit:]'
# Editor's note: This requires BSD seq, and breaks with GNU seq (see comments)

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

tom
  • 21,844
  • 6
  • 43
  • 36
  • 4
    Note that the first option with seq prints one less than the number given, so that example will print 99 `=` characters. – Camilo Martin Jan 02 '14 at 16:10
  • 18
    `printf` `tr` is the only POSIX solution because `seq`, `yes` and `{1..3}` are not POSIX. – Ciro Santilli OurBigBook.com Apr 10 '14 at 11:02
  • 4
    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
  • 5
    +1 for using no loops and only one external command (`tr`). You could also extend it to something like `printf "%${COLUMNS}s\n" | tr " " "="`. – musiphil Mar 16 '15 at 20:59
  • @CamiloMartin: I don't see that; both GNU and BSD `seq` seem to be returning the expected 100 chars. when I run `seq -s= 100 | tr -d '[:digit:]' | wc -m`. If it really doesn't work for you: what `seq` implementation are you using? – mklement0 Apr 29 '15 at 17:40
  • 1
    @mklement0 On Ubuntu 14.04.2 LTS, that comes with GNU `seq` 8.21, I get one less than the number given; [see screenshot](http://i.imgur.com/lp8kvwp.png). Now I ask on which platform you are, because I thought this was consistent. Here's [a screenshot on a RaspberryPi](http://i.imgur.com/eqH99FV.png) and [even on Windows](http://i.imgur.com/7l88JWu.png). – Camilo Martin May 03 '15 at 15:17
  • 2
    @CamiloMartin: Thanks for the follow-up: It indeed comes down to the `seq` implementation (and thus implicitly the platform): _GNU_ `seq` (Linux) produces _1 fewer_ `=` than the number specified (unlike what I originally claimed, but as you've correctly determined), whereas _BSD_ `seq` (BSD-like platforms, including OSX) produces the desired number. Simple test command: `seq -s= 100 | tr -d '[:digit:]\n' | wc -c` BSD `seq` places `=` after _every_ number, _including the last_, whereas GNU seq places a _newline_ after the _last_ number, thus coming up short by 1 with respect to the `=` count. – mklement0 May 03 '15 at 16:52
  • 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 03 '15 at 20:17
  • @CamiloMartin: Agreed: the `printf` solution is the portable one - and simpler to boot. – mklement0 May 03 '15 at 23:14
  • I could not get `tput rep` to work, but the direct ANSI sequence works. I think that `tput` fails to properly handle the `termcap` syntax of `rep=%p1%c\E[%p2%{1}%-%db` or Gnome Terminal's Termcap is missing the entry. See my answer: https://stackoverflow.com/a/59251780/7939871 – Léa Gris Dec 10 '19 at 02:16
  • The standard-conforming use of `printf` worked fine under the POSIX shell for me. Many thanks. – Bob Jarvis - Слава Україні Oct 02 '20 at 16:18
80

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.

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, and perl 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 BSD awk (as also found on OSX) - it's noticeably slower than gawk (GNU Awk) and especially mawk.
  • 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 '\0' '=' >"$outFile"
  done


  title '[M   ] seq -f [Sam Salisbury]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    seq -f '=' -s '' $COUNT_REPETITIONS >"$outFile"
  done


  title '[M   ] jot -b [Stefan Ludwig]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    jot -s '' -b '=' $COUNT_REPETITIONS >"$outFile"
  done


  title '[M   ] yes + head + tr [Digital Trauma]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    yes = | head -$COUNT_REPETITIONS | tr -d '\n'  >"$outFile"
  done

  title '[M   ] Perl [sid_com]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    perl -e "print \"=\" x $COUNT_REPETITIONS" >"$outFile"
  done

  title '[S, P] dd + tr [mklement0]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    dd if=/dev/zero bs=$COUNT_REPETITIONS count=1 2>/dev/null | tr '\0' "=" >"$outFile"
  done

  # !! On OSX, awk is BSD awk, and mawk and gawk were installed later.
  # !! On Linux systems, awk may refer to either mawk or gawk.
  for awkBin in awk mawk gawk; do
    if [[ -x $(command -v $awkBin) ]]; then

      title "[M   ] $awkBin"' - $(count+1)="=" [Steven Penny (variant)]'
      [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
      time for (( n = 0; n < COUNT_RUNS; n++ )); do 
        $awkBin -v count=$COUNT_REPETITIONS 'BEGIN { OFS="="; $(count+1)=""; print }' >"$outFile"
      done

      title "[M, P] $awkBin"' - while loop [Steven Penny]'
      [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
      time for (( n = 0; n < COUNT_RUNS; n++ )); do 
        $awkBin -v count=$COUNT_REPETITIONS 'BEGIN { while (i++ < count) printf "=" }' >"$outFile"
      done

    fi
  done

  title '[M   ] printf + bash global substr. replacement [Tim]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  # !! In Bash 4.3.30 a single run with repeat count of 1 million took almost
  # !! 50 *minutes*(!) to complete; n Bash 3.2.57 it's seemingly even slower -
  # !! didn't wait for it to finish.
  # !! Thus, this test is skipped for counts that are likely to be much slower
  # !! than the other tests.
  skip=0
  [[ $BASH_VERSINFO -le 3 && COUNT_REPETITIONS -gt 1000 ]] && skip=1
  [[ $BASH_VERSINFO -eq 4 && COUNT_REPETITIONS -gt 10000 ]] && skip=1
  if (( skip )); then
    echo 'n/a' >&2
  else
    time for (( n = 0; n < COUNT_RUNS; n++ )); do 
      { printf -v t "%${COUNT_REPETITIONS}s" '='; printf %s "${t// /=}"; } >"$outFile"
    done
  fi
} 2>&1 | 
 sort -t$'\t' -k2,2n | 
   awk -F $'\t' -v count=$COUNT_RUNS '{ 
    printf "%s\t", $1; 
    if ($2 ~ "^n/a") { print $2 } else { printf "%.4f\n", $2 / count }}' |
     column -s$'\t' -t
Community
  • 1
  • 1
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 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 04:25
  • `In order to use brace expansion with a variable, we must use \`eval\`` – pyb May 15 '19 at 01:01
  • 2
    So the perl solution (sid_com) is basically the fastest ... once the initial overhead of launching perl is reached. (it goes from 59ms for a small repeat to 67ms for a million repeats... so the perl forking took approximately 59ms on your system) – Olivier Dulac Feb 13 '20 at 17:39
  • Funny, that using the char `*` result of my list of files in the current directory being grabbed to the variable when doing: `myvar=$(printf -- '*%.0s' {1..5})` – Ricky Levi Aug 24 '22 at 08:59
  • @RickyLevi, that only happens if you use `$myvar` _unquoted_ in a later command, in which case _pathname expansion_ (globbing) predictably happens. Use `echo "$myvar"` to see that `$myvar` itself was correctly filled with verbatim ``*****. – mklement0 Aug 24 '22 at 12:16
61

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 '\0' '='
printf %100s | tr " " "="
Eugene Yarmash
  • 142,882
  • 41
  • 325
  • 378
  • 3
    ++ 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
  • 1
    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 09:09
  • Somewhat surprisingly: `dd if=/dev/zero count=1 bs=100000000 | tr '\0' '=' >/dev/null` is significantly slower than the `head -c100000000 < /dev/zero | tr '\0' '=' >/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 '\0' '=' >/dev/null` => `0,21332 s, 469 MB/s`; For: `dd if=/dev/zero count=100 bs=1000000| tr '\0' '=' >/dev/null` => `0,161579 s, 619 MB/s`; – 3ED Aug 18 '18 at 16:26
  • 1
    Used `printf`/`tr` to print caption and '=' line underneath: `CAPTION="Test Suite Results" && echo "${CAPTION}" && printf "%${#CAPTION}s\n" | tr " " "=" && echo` Works on Mac & Linux just fine. – Martyn Davis Mar 21 '22 at 21:38
38

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!

Zombo
  • 1
  • 62
  • 391
  • 407
Sam Salisbury
  • 1,066
  • 11
  • 19
  • 12
    This gives me `seq: format ‘#’ has no % directive`. `seq` is for numbers, not strings. See https://www.gnu.org/software/coreutils/manual/html_node/seq-invocation.html – John B Jul 07 '14 at 08: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 08 '14 at 09:20
  • I'm using seq from GNU coreutils. – John B Jul 08 '14 at 11:38
  • 3
    @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
  • 1
    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
  • Between `-f` and `"#"`, the space is _not_ required, because the option-argument is _nonempty_. It _is_ required after `-s`, because it's _technically_ impossible to pass the empty string as an option-argument by directly adjoining it to `-s`: the _shell_ removes the quotes from string `-s''` before passing it to `seq`, so `seq` would only see `-s`. `#` is not the only character that needs quoting - any of the so-called shell metacharacters and `*` do as well; best to simply always quote. – mklement0 Apr 29 '15 at 17:33
  • This doesn't seem to work with Unicode characters (box drawing characters in my case). `BD_H="\xE2\x94\x80"; repeat "${BD_H}" 20` renders 20 spaces. – Andris Apr 27 '17 at 10:45
  • Gives me error: `seq: invalid format string: `#'` – Ricky Levi Aug 24 '22 at 08:56
28

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.

Digital Trauma
  • 15,475
  • 3
  • 51
  • 83
  • 2
    Nicely done; please note that _BSD_ `paste` inexplicably requires `-d '\0'` for specifying an empty delimiter, and fails with `-d ''` - `-d '\0'` 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
  • @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 '\0' -; 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 06:44
17

There is no simple way. Avoid loops using printf and substitution.

str=$(printf "%40s")
echo ${str// /rep}
# echoes "rep" 40 times.
Zombo
  • 1
  • 62
  • 391
  • 407
Tim
  • 13,904
  • 10
  • 69
  • 101
  • 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 07 '13 at 18:42
  • 1
    @mklement0 Nice of you to provide function versions of both solutions, +1 on both! – Camilo Martin Jan 02 '14 at 12:16
  • 1
    A great solution that doesn't involve external programs. I would use `printf -v str …` instead of `str=$(printf …)` to avoid invoking a subshell, though. And for a general solution, I would use `printf "%s" "${str// /rep}"` instead of `echo`, because `printf` is more robust and doesn't choke on strings starting with `-` like `echo` does. – musiphil May 08 '21 at 18:59
15

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.

manifestor
  • 1,352
  • 6
  • 19
  • 34
  • Now that is unusual, if you don't ming extra space-backspaces in it.. or clean it up using: echo -e $_{1..100}'\b=' | col – anthony Aug 20 '19 at 07:02
  • 4
    [Bad idea.](https://stackoverflow.com/a/58779680/68587) This will fail if `$_1`, `$_2`, or any other of the hundred variables have values. – John Kugelman Nov 09 '19 at 13:30
  • 2
    @JohnKugelman echo $( set --; eval echo -e \${{1..100}}'\\b=' ) – mug896 Feb 29 '20 at 01:54
12

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 variable varname.
  • '%*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 of var 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}"
}
gniourf_gniourf
  • 44,650
  • 9
  • 93
  • 104
  • 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
10
#!/usr/bin/awk -f
BEGIN {
  OFS = "="
  NF = 100
  print
}

Or

#!/usr/bin/awk -f
BEGIN {
  while (z++ < 100) printf "="
}

Example

Zombo
  • 1
  • 62
  • 391
  • 407
  • 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
  • 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
  • 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. –  Aug 25 '15 at 04:54
  • 2
    Note to users - `original-awk` is non standard and not recommended – Zombo 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
  • @user2350426 : I believe this happens because original awk was limited to 99 fields per line (.. and this is annoying in a lot of cases :( ) – Olivier Dulac Feb 14 '20 at 10:12
10

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.

Geoff Nixon
  • 4,697
  • 2
  • 28
  • 34
  • 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
  • 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 03 '15 at 14:52
10

Here's what I use to print a line of characters across the screen in linux (based on terminal/screen width)

Print "=" across the screen:

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

Explanation:

Print an equal sign as many times as the given sequence:

printf '=%.0s' #sequence

Use the output of a command (this is a bash feature called Command Substitution):

$(example_command)

Give a sequence, I've used 1 to 20 as an example. In the final command the tput command is used instead of 20:

seq 1 20

Give the number of columns currently used in the terminal:

tput cols
Community
  • 1
  • 1
mattbell87
  • 565
  • 6
  • 9
8

Another mean to repeat an arbitrary string n times:

Pros:

  • Works with POSIX shell.
  • Output can be assigned to a variable.
  • Repeats any string.
  • Very fast even with very large repeats.

Cons:

  • Requires Gnu Core Utils's yes command.
#!/usr/bin/sh
to_repeat='='
repeat_count=80
yes "$to_repeat" | tr -d '\n' | head -c "$repeat_count"

With an ANSI terminal and US-ASCII characters to repeat. You can use an ANSI CSI escape sequence. It is the fastest way to repeat a character.

#!/usr/bin/env bash

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

Or statically:

Print a line of 80 times =:

printf '=\e[80b\n'

Limitations:

  • Not all terminals understands the repeat_char ANSI CSI sequence.
  • Only US-ASCII or single-byte ISO characters can be repeated.
  • Repeat stops at last column, so you can use a large value to fill a whole line regardless of terminal width.
  • The repeat is only for display. Capturing output into a shell variable will not expand the repeat_char ANSI CSI sequence into the repeated character.
Léa Gris
  • 17,497
  • 4
  • 32
  • 41
  • 1
    Minor note - REP (CSI b) should wrap around normally if the terminal is in wrapping mode. – jerch Jan 02 '20 at 22:49
6

Another bash solution using printf and tr

nb. before I begin:

  • Do we need another answer? Probably not.
  • Is this answer here already? Can't see it, so here goes.

Use the leading-zero-padding feature of printf and convert the zeroes using tr. This avoids any {1..N} generator:

$ printf '%040s' | tr '0' '='
========================================

To set the width to 'N' characters and customise the char printed:

#!/usr/bin/env bash
N=40
C='-'
printf "%0${N}s" | tr '0' "${C}"

For large N, this is quite a bit more performant than the generator; On my machine (bash 3.2.57):

$ time printf '=%.0s' {1..1000000}         real: 0m2.580s
$ time printf '%01000000s' | tr '0' '='    real: 0m0.577s
Ed Randall
  • 6,887
  • 2
  • 50
  • 45
5

I guess the original purpose of the question was to do this just with the shell's built-in commands. So for loops and printfs 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 \/\/\/\/\/\/\/\/\/\/\/\/

phuclv
  • 37,963
  • 15
  • 156
  • 475
Stefan Ludwig
  • 333
  • 2
  • 8
  • 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
  • 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 provide `jot`. – agc Feb 08 '19 at 20:54
5

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 of printf. And I read the problem description in this question as expressing a preference to print with echo. The above function definition works in bash and ksh93. Although printf 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 for echo. zsh does this. And there definitely exist echos that don't recognize -n, as it is not standard. (Many Bourne-style shells don't accept C-style for loops at all, thus their echo 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.

Community
  • 1
  • 1
Eliah Kagan
  • 1,704
  • 3
  • 22
  • 38
  • 2
    Strongly avoid doing loop versions. `printf "%100s" | tr ' ' '='` is optimal. – ocodo Nov 04 '14 at 05: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
  • Adding parentheses around your shorter loop preserves the value of n without affecting the echos: `(while ((n--)); do echo -n "$s"; done; echo)` –  Aug 23 '15 at 04:24
  • use printf instead of echo! it is way more portable (echo -n can work only on some systems). see https://unix.stackexchange.com/questions/65803/why-is-printf-better-than-echo/65819#65819 (one of Stephane Chazelas's awesome answer) – Olivier Dulac Feb 13 '20 at 17:21
  • @OlivierDulac The question here is about bash. No matter what operating system you are running, *if you are using bash on it*, bash has an `echo` builtin that supports `-n`. The spirit of what you are saying is absolutely correct. `printf` should almost always be preferred to `echo`, at least in non-interactive use. But I don't think it was in any way inappropriate or misleading to give an `echo` answer to a question that asked for one *and that gave enough information to know that it would work*. Please note also that support for `((n--))` (without a `$`) is itself not guaranteed by POSIX. – Eliah Kagan Feb 14 '20 at 02:01
  • @EliahKagan I never said it was innapropriate or misleading to give an echo answer, I warn that we (all) should stick to using printf insterad of echo, as printf is more portable (+ it avoids lots of side effects depending on the beginning of the thing echo'ed (minuses, etc), see stephane's answer). + that way the answer is not limited to bash-and-similar shells (btw, OP said bash but could have meant "shell") – Olivier Dulac Feb 14 '20 at 10:06
5

In bash 3.0 or higher

for i in {1..100};do echo -n =;done
loafoe
  • 363
  • 2
  • 11
4

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.

phuclv
  • 37,963
  • 15
  • 156
  • 475
loevborg
  • 1,774
  • 13
  • 18
4

A more elegant alternative to the proposed Python solution could be:

python -c 'print "="*(1000)'
Anas Tiour
  • 1,344
  • 3
  • 17
  • 33
3

Simplest is to use this one-liner in csh/tcsh:

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

double-beep
  • 5,031
  • 17
  • 33
  • 41
2
repeat() {
    # $1=number of patterns to repeat
    # $2=pattern
    printf -v "TEMP" '%*s' "$1"
    echo ${TEMP// /$2}
}
ZoogieZork
  • 11,215
  • 5
  • 45
  • 42
WSimpson
  • 460
  • 4
  • 7
2

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.

Xennex81
  • 389
  • 2
  • 6
  • In bash and with a positive n: `while (( i-- )); do echo -n " "; done` works. –  Aug 23 '15 at 04:27
2

Another option is to use GNU seq and remove all numbers and newlines it generates:

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

This command prints the # character 100 times.

sigalor
  • 901
  • 11
  • 24
2

Not to pile-on, but another pure-Bash approach takes advantage of ${//} substitution of arrays:

$ arr=({1..100})
$ printf '%s' "${arr[@]/*/=}"
====================================================================================================
dimo414
  • 47,227
  • 18
  • 148
  • 244
1

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========  
Camilo Martin
  • 37,236
  • 20
  • 111
  • 154
Raul Baron
  • 67
  • 1
  • 7
  • 1
    `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 07 '15 at 12:55
1
function repeatString()
{
    local -r string="${1}"
    local -r numberToRepeat="${2}"

    if [[ "${string}" != '' && "${numberToRepeat}" =~ ^[1-9][0-9]*$ ]]
    then
        local -r result="$(printf "%${numberToRepeat}s")"
        echo -e "${result// /${string}}"
    fi
}

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

Nam Nguyen
  • 5,668
  • 14
  • 56
  • 70
1
for i in {1..100}
do
  echo -n '='
done
echo
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
1

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.

DaBler
  • 2,695
  • 2
  • 26
  • 46
1

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}
}
1

Simplest is to use this one-liner in bash:

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

1

Most existing solutions all depend on {1..10} syntax support of the shell, which is bash- and zsh- specific, and doesn't work in tcsh or OpenBSD's ksh and most non-bash sh.

The following should work on OS X and all *BSD systems in any shell; in fact, it can be used to generate a whole matrix of various types of decorative space:

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

Sadly, we don't get a trailing newline; which can be fixed by an extra printf '\n' after the fold:

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

References:

cnst
  • 25,870
  • 6
  • 90
  • 122
1

My proposal (accepting variable values for n):

n=100
seq 1 $n | xargs -I {} printf =
Sopalajo de Arrierez
  • 3,543
  • 4
  • 34
  • 52
1

Slightly longer version, but if you have to use pure Bash for some reason, you can use a while loop with an incrementing variable:

n=0; while [ $n -lt 100 ]; do n=$((n+1)); echo -n '='; done
Luke Mlsna
  • 468
  • 4
  • 16
1

here's an awk-based repeater that's

  • fully POSIX-complaint,
  • variable-length-friendly,

completely loop-less (in fact, doesn't even have to spend time calculating the desired length unless you wanna customize it), and

  • non-awk-variant specific ::

.

jot 15 | mawk2 'NF += OFS = $_'   

11
222
3333
44444
555555
6666666
77777777
888888888
9999999999
1010101010101010101010
111111111111111111111111
12121212121212121212121212
1313131313131313131313131313
141414141414141414141414141414
15151515151515151515151515151515

or just repeat the last one :

jot - 1 25 3 |

mawk2 'NF=NR^(OFS=$NF)^_' FS= 

1
44
777
10000
133333
1666666
19999999
222222222
2555555555

…… or repeat ASCII punctuation symbols with a curvature ?

jot  -c  -  34 126 | gtr -cd '[:punct:]\n' |
nonEmpty           | rs -C'8' 0 2 |gtr -d '8' | 

mawk2 'NF+=_*(OFS=__=$NF)+int(NR*(1+sin(log(NR))))' FS='^$' 

"#"#
$%$%$%$%
&'&'&'&'&'&'
()()()()()()()()
*+*+*+*+*+*+*+*+*+*+
,-,-,-,-,-,-,-,-,-,-,-,-
././././././././././././././
:;:;:;:;:;:;:;:;:;:;:;:;:;:;:;
<=<=<=<=<=<=<=<=<=<=<=<=<=<=<=<=<=
>?>?>?>?>?>?>?>?>?>?>?>?>?>?>?>?>?>?
@[@[@[@[@[@[@[@[@[@[@[@[@[@[@[@[@[@[@[
\]\]\]\]\]\]\]\]\]\]\]\]\]\]\]\]\]\]\]\]
^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_^_
`{`{`{`{`{`{`{`{`{`{`{`{`{`{`{`{`{`{`{`{`{
|}|}|}|}|}|}|}|}|}|}|}|}|}|}|}|}|}|}|}|}|}|}
~~~~~~~~~~~~~~~~~~~~~~

or perhaps make horizontal bar charts that resemble the normal distribution :

jot  -c - 34 126  | gtr -cd '0-9A-F\n' | 
 
mawk2 'NF && (NF+=_*(OFS=$NF)+exp(cos(NR/atan2(0,-1)))^4.1)'  FS='^$'

00
11111
22222222222222
333333333333333333333333333333333
444444444444444444444444444444444444444444444444444444
555555555555555555555555555555555555555555555555555555555555
66666666666666666666666666666666666666666666
7777777777777777777777
888888888
999
A
B
C
DD
EEEEEEE
FFFFFFFFFFFFFFFFFF

… if you're a gambler at heart and wanna have it repeat something related to euler e^pi but also includes randomization, then

jot 20 | 

mawk2 'NF=NR+_*(OFS=sprintf("%.f",((NR*rand()*4))*exp(atan2(0,-1))))'

|

1
2111
35252
4220220220
5177177177177
6405405405405405
7543543543543543543
8649649649649649649649
9503503503503503503503503
10555555555555555555
11669669669669669669669669669669
12110110110110110110110110110110110
13917917917917917917917917917917917917
141188118811881188118811881188118811881188118811881188
1510151015101510151015101510151015101510151015101510151015
16521521521521521521521521521521521521521521521
17900900900900900900900900900900900900900900900900
1866666666666666666
19159159159159159159159159159159159159159159159159159159
20182182182182182182182182182182182182182182182182182182182

…. or straight up letters repeating based on something that's correlated with golden ratio but not the same :

jot  -c  -  34 126  | gtr -cd 'A-Za-z\n' |shuf | shuf | 
rs -t -C= 0 3 | gtr -d '=' |  

mawk2 'NF && (NF=NR^(exp(1)/(1+sqrt(5)))+_*(OFS=$_ ($_=_)))'  FS='^$' 

|

qfd
NUONUO
WEvWEv
sCRsCRsCR
LoVLoVLoVLoV
putputputput
SYjSYjSYjSYjSYj
XcQXcQXcQXcQXcQ
hGrhGrhGrhGrhGrhGr
IyAIyAIyAIyAIyAIyAIyA
lZalZalZalZalZalZalZa
FMJFMJFMJFMJFMJFMJFMJFMJ
mwTmwTmwTmwTmwTmwTmwTmwT
DenDenDenDenDenDenDenDenDen
PHPHPHPHPHPHPHPHPH
iziziziziziziziziziz

…..just the last letter

 jot  -c  -  34 126  | gtr -cd 'A-Za-z\n' |shuf | shuf | 
 rs -t -C= 0 2 | gtr -d '=' |  

 mawk2 'NF && (NF=NR^(exp(1.1)/(1+sqrt(5)))+_*(OFS=$NF))'  FS=       

.

l
A
kii
GIII
znnnn
fFFFFF
aHHHHHH
sSSSSSS
jooooooo
pTTTTTTTT
bDDDDDDDDD
Yvvvvvvvvvv
MCCCCCCCCCC
WBBBBBBBBBBB
JXXXXXXXXXXXX
mNNNNNNNNNNNNN
deeeeeeeeeeeee
xQQQQQQQQQQQQQQ
thhhhhhhhhhhhhhh
yOOOOOOOOOOOOOOOO
Puuuuuuuuuuuuuuuu
qRRRRRRRRRRRRRRRRR
rLLLLLLLLLLLLLLLLLL
VKKKKKKKKKKKKKKKKKKK
wUUUUUUUUUUUUUUUUUUU
ZEEEEEEEEEEEEEEEEEEEE

…and last but not least, fully UTF-8 friendly, even emojis : |

 
  
   
    

It starts to slowdown when the reps needed is above 100 million or so, but short of that, this can be somewhat handy for essentially matching the convenience of languages with dedicated string repeat operators like python ( * ) or perl ( x )

beware though : it's as dangerous as it is efficacious :

jot - 1 90 8  | 

mawk2 '(OFS="")(NF+=\
        ((sqrt(NR)))^(20.5/6.0625\
        )^sqrt(sqrt(log((OFS=$!!NF))^2)))' FS='^$' OFS= | 

mawk -Wi -- '{  print "\t", 8*(NR)-7,
                        length($0),
               sprintf("%.50s",$0) }'

 1 2 11
 9 9 999999999
 17 144 17171717171717171717171717171717171717171717171717
 25 954 25252525252525252525252525252525252525252525252525
 33 5146 33333333333333333333333333333333333333333333333333
 41 23526 41414141414141414141414141414141414141414141414141
 49 94378 49494949494949494949494949494949494949494949494949
 57 340432 57575757575757575757575757575757575757575757575757
 65 1124212 65656565656565656565656565656565656565656565656565
 73 3445508 73737373737373737373737373737373737373737373737373
 81 9904644 81818181818181818181818181818181818181818181818181
 89 26930222 89898989898989898989898989898989898989898989898989

It can grow really rapidly if you only change the parameters a bit.

RARE Kpop Manifesto
  • 2,453
  • 3
  • 11
0
n=5; chr='x'; chr_string='';
for (( i=0; $i<$n; i++ ))
do
    chr_string=$chr_string$chr
done
echo -n "$chr_string"

Works for...
n=integer (inclusive of zero and negetive).
chr=printable and white space (space and tab).

NOYB
  • 625
  • 8
  • 14
0

You can do this without invoking an external command. First you prepend 0 to a 0 until it counts a len of 100, then you replace zero with the character you want.

LEN=100
foo="$(builtin printf "%0${LEN}d" 0)"
echo "${foo//0/=}"
Alai
  • 123
  • 1
  • 8
-1
printf -- '=%.0s' {1..100}

The double dash -- means "End of command line flags", so don't try to parse what comes after command line options.

If you want to print the dash - character, rather than the = character, multiple times and don't include the double dash -- this is what you'll get:

$ printf '-%.0s' {1..100}
bash: printf: -%: invalid option
printf: usage: printf [-v var] format [arguments]

Why not create a one line function like this:

function repeat() { num="${2:-100}"; printf -- "$1%.0s" $(seq 1 $num); }

Then, you can call it like this:

$ repeat -
----------------------------------------------------------------------------------------------------

or like that:

$ repeat =
====================================================================================================

or like this:

$ repeat '*' 8
********
l3x
  • 30,760
  • 1
  • 55
  • 36