Creating string of repeated characters in shell script [duplicate]
Asked Answered
T

9

58

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?

Thirion answered 9/7, 2010 at 10:50 Comment(5)
Related on Super User: superuser.com/q/86340/269404Parturition
Duplicates: https://mcmap.net/q/75753/-how-can-i-repeat-a-character-in-bash/2157640 https://mcmap.net/q/75754/-print-a-character-repeatedly-in-bash-duplicate/2157640Parturition
In zsh at least, {1..$length} works just fine.Sarcomatosis
n=15; chr='.'; txt=''; for((i=0; $i<$n; i++)); do txt=$txt$chr; done; echo "$txt"Occupancy
This is so badly needed when I use zsh bindkey with ^[[D to move the cursor back into a useful place!Postgraduate
C
90

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'
Cornstarch answered 6/5, 2011 at 19:5 Comment(6)
+1 for a sweet and portable solution without filthy bashisms or hundreds of processes being spawnedUpbraid
you can use /dev/zero directly, no need to redirectSumikosumma
Not that sweet and portable: "head: illegal option -- c" (SunOS 5.10 Generic_150401-20 i86pc i386 i86pc)Fireball
head -c is portable only on GNU and BSD utils, it's not POSIX.Sergiosergipe
You can use dd instead with dd if=/dev/zero bs=1 count=16 replacind the head part.Misdoing
head -n is POSIXDihedron
S
45
len=100 ch='#'
printf '%*s' "$len" | tr ' ' "$ch"
Saturation answered 9/7, 2010 at 12:51 Comment(4)
This is the shortest POSIX 7 solution I have seen so far.Caligula
can be even shorter, take out the quotation in "$len" to be $len ;)Punch
shellcheck says you are risking code injection without the quotes on "$len".Housewares
@JesseChisholm using shellcheck you are risking corporate code into someone else's hands ;)Prosecutor
R
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):

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

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

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

-Blatantly stolen from dogbane's answer https://mcmap.net/q/75753/-how-can-i-repeat-a-character-in-bash

Edit: 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 """""""".

Recap answered 15/4, 2014 at 15:4 Comment(8)
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)Preliminary
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 statementPreliminary
This only works for me in my shell (bash) if I use eval: VAR=15 ; eval printf '=%.0s' {1..$VAR}Curet
Only works for me with eval too. Replacing $VAR with $COLUMNS is very useful for making a horizontal ruler. If needed from inside a script: hr () { eval printf '=%.0s' {1..$(tput cols)}; echo; }Dendriform
Your answer is fine, using POSIX shell, but using bash , you could avoid one fork by using -v arg to printf -v line '.%.0s' {1..$VAR}Medrano
@Curet In bash, you can't use variables in a brace expansion. Instead, you can use $(seq 1 $VAR) in place of {1..$VAR}Crossbar
@Crossbar actually it does... tested exactly what I quoted above just now using bash version 4.3.30(1)-release on Debian 8 - maybe you have a different shell ~$ VAR=15 ; eval printf '=%.0s' {1..$VAR} (line break was here) ===============Curet
@Curet I meant without using eval. eval is not safe to use in a script. #17529720Crossbar
F
9

On most systems, you could get away with a simple

N=100
myvar=`perl -e "print '.' x $N;"`
Faller answered 9/7, 2010 at 11:25 Comment(0)
F
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://mcmap.net/q/75754/-print-a-character-repeatedly-in-bash-duplicate

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:

....................
Foreclosure answered 24/9, 2014 at 23:49 Comment(1)
n=0 fails to produce the correct number of characters. As do most of the printf solutions.Archipenko
P
4

The solution without loops:

N=100
myvar=`seq 1 $N | sed 's/.*/./' | tr -d '\n'`
Patmos answered 9/7, 2010 at 12:4 Comment(0)
E
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// /#}
Endure answered 9/7, 2010 at 15:16 Comment(0)
L
2
num=100
myvar=$(jot -b . -s '' $num)
echo $myvar
Lattonia answered 10/2, 2014 at 17:13 Comment(0)
S
-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:

.....
Strawworm answered 9/7, 2010 at 14:50 Comment(1)
In bash and ksh (perhaps zsh), you could write echo ${base:0:$x} -- details herePinto

© 2022 - 2024 — McMap. All rights reserved.