62

I need to generate a string of dots (.characters) as a variable.

I.e., in my Bash script, for input 15 I need to generate this string of length 15: ...............

I need to do so variably. I tried using this as a base (from Unix.com):

for i in {1..100};do printf "%s" "#";done;printf "\n"

But how do I get the 100 to be a variable?

5
  • 1
    Related on Super User: superuser.com/q/86340/269404 Commented Nov 16, 2014 at 14:32
  • 1
    Duplicates: stackoverflow.com/q/5349718/2157640 stackoverflow.com/q/5799303/2157640 Commented Nov 16, 2014 at 14:40
  • In zsh at least, {1..$length} works just fine. Commented Aug 24, 2017 at 7:33
  • n=15; chr='.'; txt=''; for((i=0; $i<$n; i++)); do txt=$txt$chr; done; echo "$txt" Commented Jan 16, 2022 at 15:30
  • This is so badly needed when I use zsh bindkey with ^[[D to move the cursor back into a useful place! Commented Jun 7, 2022 at 23:05

9 Answers 9

94

You can get as many NULL bytes as you want from /dev/zero. You can then turn these into other characters. The following prints 16 lowercase a's

head -c 16 < /dev/zero | tr '\0' '\141'
Sign up to request clarification or add additional context in comments.

6 Comments

+1 for a sweet and portable solution without filthy bashisms or hundreds of processes being spawned
you can use /dev/zero directly, no need to redirect
Not that sweet and portable: "head: illegal option -- c" (SunOS 5.10 Generic_150401-20 i86pc i386 i86pc)
head -c is portable only on GNU and BSD utils, it's not POSIX.
You can use dd instead with dd if=/dev/zero bs=1 count=16 replacind the head part.
|
47
len=100 ch='#'
printf '%*s' "$len" | tr ' ' "$ch"

3 Comments

This is the shortest POSIX 7 solution I have seen so far.
can be even shorter, take out the quotation in "$len" to be $len ;)
shellcheck says you are risking code injection without the quotes on "$len".
28

Easiest and shortest way without a loop

VAR=15

Prints as many dots as VAR says (change the first dot to any other character if you like) with eval :

eval printf '.%.0s' {1..$VAR}

Saves the dotted line in a variable to be used later:

line=$(eval printf '.%.0s' {1..$VAR})
echo "Sign here $line"

-Blatantly stolen from dogbane's answer https://stackoverflow.com/a/5349842/3319298

Edit 1 Note about brace expansions like {1..N} - they are performed early and do NOT expand $VAR, thus the use of eval. The use of eval in a script has security implications, be cautious.

Edit 2: Since I have now switched to fish shell, here is a function defined in config.fish that does this with convenience in that shell:

function line -a char -a length
  printf '%*s\n' $length "" | tr ' ' $char
end

Usage: line = 8 produces ========, line \" 8 produces """""""".

8 Comments

what an interesting way to exploit printf's shell feature that repeatibly consumes arguments until satisfied in combination with a zero-field width, forcing just the string before the % to be printed. This could be used as a trick to divide the count as well (adding the %.0s twice to force it to consume 2 arguments per pass)
oh yeah and I forgot something important, since printf processes %s with a null if no args are given, a value of zero would be a problem, for that you would have to add an if statement
This only works for me in my shell (bash) if I use eval: VAR=15 ; eval printf '=%.0s' {1..$VAR}
@6EQUJ5 In bash, you can't use variables in a brace expansion. Instead, you can use $(seq 1 $VAR) in place of {1..$VAR}
@6EQUJ5 I meant without using eval. eval is not safe to use in a script. stackoverflow.com/questions/17529220/…
|
10

On most systems, you could get away with a simple

N=100
myvar=`perl -e "print '.' x $N;"`

Comments

6

I demonstrated a way to accomplish this task with a single command in another question, assuming it's a fixed number of characters to be produced.

I added an addendum to the end about producing a variable number of repeated characters, which is what you asked for, so my previous answer is relevant here:

https://stackoverflow.com/a/17030976/2284005

I provided a full explanation of how it works there. Here I'll just add the code to accomplish what you're asking for:

    n=20 # This the number of characters you want to produce

    variable=$(printf "%0.s." $(seq 1 $n)) # Fill $variable with $n periods

    echo $variable # Output content of $variable to terminal

Outputs:

....................

1 Comment

n=0 fails to produce the correct number of characters. As do most of the printf solutions.
5

The solution without loops:

N=100
myvar=`seq 1 $N | sed 's/.*/./' | tr -d '\n'`

Comments

4

You can use C-style for loops in Bash:

num=100
string=$(for ((i=1; i<=$num; i++));do printf "%s" "#";done;printf "\n")

Or without a loop, using printf without using any externals such as sed or tr:

num=100
printf -v string "%*s" $num ' ' '' $'\n'
string=${string// /#}

Comments

3
num=100
myvar=$(jot -b . -s '' $num)
echo $myvar

Comments

-1

When I have to create a string that contains $x repetitions of a known character with $x below a constant value, I use this idiom:

base='....................'
# 0 <= $x <= ${#base}
x=5
expr "x$base" : "x\(.\{$x\}\)"    # Will output '\n' too

Output:

.....

1 Comment

In bash and ksh (perhaps zsh), you could write echo ${base:0:$x} -- details here

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.