Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Bash: fast way to repeat string???

31 views
Skip to first unread message

liha...@gmail.com

unread,
Dec 31, 2007, 2:48:44 AM12/31/07
to
Is there any bash commands to repeat a string, i.e. I want to output
60 '#' (just an example, need to repeat an arbitrary string anyway) in
a single line, say

echo " ##################...[cut]...##"

which is awkward, is there any fast way to achieve this??

many thanks,
lihao

Icarus Sparry

unread,
Dec 31, 2007, 3:03:29 AM12/31/07
to

If you are in emacs mode, then typing the following sequence of characters

e c h o space " space esc 6 esc 0 # "

will do what you ask for for a single character. You can set up a macro
that inserts a string and repeat that for the arbitrary string case.

Control-X ( a b c Control-X ) esc 1 esc 9 control-X control-E

to insert 20 copies of "abc".

Or you could write a loop.
Or you use your favorite editor and its facilities to repeat things and
write the command in a script file.

Chris F.A. Johnson

unread,
Dec 31, 2007, 3:53:21 AM12/31/07
to

printf -v rept "%60s" ' ' # Older versions: rept=$( printf "%60s" ' ' )
rept=${rept//?/#}


Or:

rept=#
while [ ${#rept} -lt 60 ]
do
rept=$rept$rept$rept
done
repr=${rept:0:60}


Or, for any POSIX shell:

rept=#
while [ ${#rept} -lt 60 ]
do
rept=$rept$rept$rept
done
while [ ${#rept} -gt 60 ]
do
rept=${rept#?}
done


Or:

printf "%60s\n" " " | tr ' ' '#'


Or.....

--
Chris F.A. Johnson, author <http://cfaj.freeshell.org/shell/>
Shell Scripting Recipes: A Problem-Solution Approach (2005, Apress)
===== My code in this post, if any, assumes the POSIX locale
===== and is released under the GNU General Public Licence

Janis Papanagnou

unread,
Dec 31, 2007, 2:13:52 PM12/31/07
to
Icarus Sparry wrote:
> On Sun, 30 Dec 2007 23:48:44 -0800, liha...@gmail.com wrote:
>
>
>>Is there any bash commands to repeat a string, i.e. I want to output 60
>>'#' (just an example, need to repeat an arbitrary string anyway) in a
>>single line, say
>>
>> echo " ##################...[cut]...##"
>>
>>which is awkward, is there any fast way to achieve this??
>>
>>many thanks,
>>lihao
>
>
> If you are in emacs mode, then typing the following sequence of characters
>
> e c h o space " space esc 6 esc 0 # "

Hmm.. - in vi/vim and kornshell's vi mode it would be...

e c h o space " space " esc 6 0 i # esc

...but bash seems not to support that. (Just wondering.)

>
> will do what you ask for for a single character. You can set up a macro
> that inserts a string and repeat that for the arbitrary string case.
>
> Control-X ( a b c Control-X ) esc 1 esc 9 control-X control-E
>
> to insert 20 copies of "abc".
>
> Or you could write a loop.
> Or you use your favorite editor and its facilities to repeat things and
> write the command in a script file.

I suppose the OP wants some terse script expression like echo "#"{60}
(similar to perl's 'x 60') which doesn't seem to be supported in bash.

So I'd resort to s=$( printf "%60s" ); echo " ${s// /#}"

Janis

liha...@gmail.com

unread,
Dec 31, 2007, 2:31:14 PM12/31/07
to
Hi, thanks both for your hints:- )

On Dec 31, 3:53 am, "Chris F.A. Johnson" <cfajohn...@gmail.com> wrote:


> On 2007-12-31, lihao0...@gmail.com wrote:
>
> > Is there any bash commands to repeat a string, i.e. I want to output
> > 60 '#' (just an example, need to repeat an arbitrary string anyway) in
> > a single line, say
>
> >     echo " ##################...[cut]...##"
>
> > which is awkward, is there any fast way to achieve this??
>
> printf -v rept "%60s" ' ' # Older versions: rept=$( printf "%60s" ' ' )
> rept=${rept//?/#}
>
>    Or:

Very nice, I finally came up with a solution like:

A=$(seq 60)
B=${A//??/#}

works pretty well. :-)

lihao

liha...@gmail.com

unread,
Dec 31, 2007, 2:38:03 PM12/31/07
to
On Dec 31, 2:13 pm, Janis Papanagnou <Janis_Papanag...@hotmail.com>
wrote:
> Icarus Sparry wrote:

Thanks, you are right, and I want this to show up in my bash
script :- )

"printf" is much better than "seq" I used. :-)

lihao

Janis Papanagnou

unread,
Dec 31, 2007, 2:37:59 PM12/31/07
to
liha...@gmail.com wrote:
> Hi, thanks both for your hints:- )
>
> On Dec 31, 3:53 am, "Chris F.A. Johnson" <cfajohn...@gmail.com> wrote:
>
>>On 2007-12-31, lihao0...@gmail.com wrote:
>>
>>
>>>Is there any bash commands to repeat a string, i.e. I want to output
>>>60 '#' (just an example, need to repeat an arbitrary string anyway) in
>>>a single line, say
>>
>>> echo " ##################...[cut]...##"
>>
>>>which is awkward, is there any fast way to achieve this??
>>
>>printf -v rept "%60s" ' ' # Older versions: rept=$( printf "%60s" ' ' )
>>rept=${rept//?/#}
>>
>> Or:
>
>
> Very nice, I finally came up with a solution like:
>
> A=$(seq 60)
> B=${A//??/#}
>
> works pretty well. :-)

But that will not produce a sequence of 60 #'es.
Compare the two outputs...

$ A=$(seq 60) ; B=${A//??/#} ; echo ${#B}
85

$ A=$(printf "%60s") ; B=${A// /#} ; echo ${#B}
60

And your code will produce wrong output depending on the
length of the sequence; try 'seq 61' to see what I mean.

Janis

liha...@gmail.com

unread,
Dec 31, 2007, 2:41:27 PM12/31/07
to
On Dec 31, 2:37 pm, Janis Papanagnou <Janis_Papanag...@hotmail.com>
wrote:

ah, you are right. :-)

lihao

Icarus Sparry

unread,
Dec 31, 2007, 3:18:19 PM12/31/07
to
On Mon, 31 Dec 2007 20:13:52 +0100, Janis Papanagnou wrote:

> Icarus Sparry wrote:
>> On Sun, 30 Dec 2007 23:48:44 -0800, liha...@gmail.com wrote:
>>
>>
>>>Is there any bash commands to repeat a string, i.e. I want to output 60
>>>'#' (just an example, need to repeat an arbitrary string anyway) in a
>>>single line, say
>>>
>>> echo " ##################...[cut]...##"
>>>
>>>which is awkward, is there any fast way to achieve this??
>>>
>>>many thanks,
>>>lihao
>>
>>
>> If you are in emacs mode, then typing the following sequence of
>> characters
>>
>> e c h o space " space esc 6 esc 0 # "
>
> Hmm.. - in vi/vim and kornshell's vi mode it would be...
>
> e c h o space " space " esc 6 0 i # esc
>
> ...but bash seems not to support that. (Just wondering.)

bash is in good company, ksh doesn't seem to support it either.
The best I can do is
# esc 5 9 . I e c h o space " space esc A "

>> will do what you ask for for a single character. You can set up a macro
>> that inserts a string and repeat that for the arbitrary string case.
>>
>> Control-X ( a b c Control-X ) esc 1 esc 9 control-X control-E
>>
>> to insert 20 copies of "abc".
>>
>> Or you could write a loop.
>> Or you use your favorite editor and its facilities to repeat things and
>> write the command in a script file.
>
> I suppose the OP wants some terse script expression like echo "#"{60}
> (similar to perl's 'x 60') which doesn't seem to be supported in bash.
>
> So I'd resort to s=$( printf "%60s" ); echo " ${s// /#}"
>
> Janis

Yes, but it makes very little sense to me to run a command at run time to
build up a constant string that is known at edit time. The perl 'x'
operator is useful because it takes a variable for the number of repeats.

Obviously your solution can be extended to do this, e.g.

s=$(printf "%*s" $((4 * 5))) ; echo "${s// /#}"

Janis Papanagnou

unread,
Dec 31, 2007, 4:24:07 PM12/31/07
to
Icarus Sparry wrote:
> On Mon, 31 Dec 2007 20:13:52 +0100, Janis Papanagnou wrote:
>
>
>>Icarus Sparry wrote:
>>
>>>On Sun, 30 Dec 2007 23:48:44 -0800, liha...@gmail.com wrote:
>>>
>>>
>>>
>>>>Is there any bash commands to repeat a string, i.e. I want to output 60
>>>>'#' (just an example, need to repeat an arbitrary string anyway) in a
>>>>single line, say
>>>>
>>>> echo " ##################...[cut]...##"
>>>>
>>>>which is awkward, is there any fast way to achieve this??
>>>>
>>>>many thanks,
>>>>lihao
>>>
>>>
>>>If you are in emacs mode, then typing the following sequence of
>>>characters
>>>
>>>e c h o space " space esc 6 esc 0 # "
>>
>>Hmm.. - in vi/vim and kornshell's vi mode it would be...
>>
>> e c h o space " space " esc 6 0 i # esc
>>
>>...but bash seems not to support that. (Just wondering.)
>
>
> bash is in good company, ksh doesn't seem to support it either.

The above I've done with ksh Version M 1993-12-28 r (and as far as
I recall I've never had any problems with such commands using ksh;
but I've no ksh88 at hand to check this specific one again).

Janis

> The best I can do is
> # esc 5 9 . I e c h o space " space esc A "
>

> [...]

Message has been deleted

Janis Papanagnou

unread,
Dec 31, 2007, 4:28:18 PM12/31/07
to
Icarus Sparry wrote:
> On Mon, 31 Dec 2007 20:13:52 +0100, Janis Papanagnou wrote:
>
>
>>Icarus Sparry wrote:
>>
>>>On Sun, 30 Dec 2007 23:48:44 -0800, liha...@gmail.com wrote:
>>>
>>>
>>>
>>>>Is there any bash commands to repeat a string, i.e. I want to output 60
>>>>'#' (just an example, need to repeat an arbitrary string anyway) in a
>>>>single line, say
>>>>
>>>> echo " ##################...[cut]...##"
>>>>
>>>>which is awkward, is there any fast way to achieve this??
>>>>
>>>>many thanks,
>>>>lihao
>>>
>>>
>>>If you are in emacs mode, then typing the following sequence of
>>>characters
>>>
>>>e c h o space " space esc 6 esc 0 # "
>>
>>Hmm.. - in vi/vim and kornshell's vi mode it would be...
>>
>> e c h o space " space " esc 6 0 i # esc
>>
>>...but bash seems not to support that. (Just wondering.)
>
>
> bash is in good company, ksh doesn't seem to support it either.

You seem to be right. (Don't know what I've tried...)

> The best I can do is
> # esc 5 9 . I e c h o space " space esc A "

Janis

Janis Papanagnou

unread,
Dec 31, 2007, 4:42:00 PM12/31/07
to

Another option in ksh is to use the Esc-V command which invokes an editor
of your choice where one can do that more powerful command line editing.

Janis

pgas

unread,
Jan 3, 2008, 4:00:18 AM1/3/08
to

>> >>>Is there any bash commands to repeat a string, i.e. I want to output
>> >>>60 '#' (just an example, need to repeat an arbitrary string anyway) in
>> >>>a single line, say

another hack:

printf '#%.0s' {1..60}

--
pgas @ SDF Public Access UNIX System - http://sdf.lonestar.org

mik3...@gmail.com

unread,
Jan 4, 2008, 1:34:39 AM1/4/08
to
On Dec 31 2007, 3:48 pm, "lihao0...@gmail.com" <lihao0...@gmail.com>
wrote:

#!/bin/sh

genchar() {
num=0
while [ "$num" -lt 60 ]
do
printf "$1"
num=`echo $num + 1|bc`
done
}
genchar "#"

Icarus Sparry

unread,
Jan 4, 2008, 10:55:11 AM1/4/08
to

The original poster asked for a "fast" way. I hope that this is the
slowest way suggested. In particular the way of adding one to num will
use at least 3 processes if you are using bash, one to do the echo, one
to run bc, and one to set up the pipe.

The "expr" program is traditionally used to do arithmetic in conjunction
with the Bourne shell. It would only use 1 process.

Bash has builtin commands, see "let" and "typeset -i" to enable you to
add 1 without any external processes.

Stephane Chazelas

unread,
Jan 4, 2008, 11:46:43 AM1/4/08
to
On 04 Jan 2008 15:55:11 GMT, Icarus Sparry wrote:
[...]

>> num=`echo $num + 1|bc`
[...]

> The original poster asked for a "fast" way. I hope that this is the
> slowest way suggested. In particular the way of adding one to num will
> use at least 3 processes if you are using bash, one to do the echo, one
> to run bc, and one to set up the pipe.

Note that there are two pipes above, one induced by `...` for
bash to read the output of the pipeline and one induced by "|".

csh, ksh, pdksh and zsh will spawn only 2 processes.

ash, bash, rc, es, tcsh 3 processes.

For those who are confusing "sh" with the Bourne shell, a
reminder:

"sh" has been the name of a number of shells accross Unix
history. From the Thomson shell in the late 60s. It has been the
Bourne shell on most Unices for a very long time (over 2
decades), that's probably why many people still think that "sh"
means Bourne shell.

But since the early 90s, "sh" and the Unix utilities have their
own POSIX specification. So you may write scripts in that
language without worrying about what the interpreter would be as
long as it's a conformant one. The specification is based on a
subset of the Korn shells. Shells that are able to interpret
scripts written in that language include bash, ksh, pdksh,
moddern ash, zsh and their derivatives when called as "sh". The
Bourne shell is not such a shell. The sh specification is not
strictly backward compatible with the Bourne shell, but most of
the scripts written for the Bourne shell will work with a
standard sh interpreter as the non-backward compatible changes
are quite small.

Note a few systems like Solaris still have a Bourne shell as
/bin/sh and the standard sh must be found elsewhere.
Fortunately, most other Unices didn't go down that same path and
their /bin/sh is an implementation or the other of a standard sh
interpreter.

--
Stephane

liha...@gmail.com

unread,
Jan 4, 2008, 6:06:07 PM1/4/08
to
On Jan 4, 11:46 am, Stephane Chazelas <stephane_chaze...@yahoo.fr>
wrote:

> On 04 Jan 2008 15:55:11 GMT, Icarus Sparry wrote:
> [...]
>
> >>     num=`echo $num + 1|bc`
> [...]
> > The original poster asked for a "fast" way. I hope that this is the
> > slowest way suggested. In particular the way of adding one to num will
> > use at least 3 processes if you are using bash, one to do the echo, one
> > to run bc, and one to set up the pipe.
>
> Note that there are two pipes above, one induced by `...` for
> bash to read the output of the pipeline and one induced by "|".
>
> csh, ksh, pdksh and zsh will spawn only 2 processes.
>
> ash, bash, rc, es, tcsh 3 processes.

Interesting and very informative discussions, thanks guys for sharing
your knowledge. One more question:

under my bash 3.1.17(Ubuntu Linux 2.6.18-4-amd64), if I issue the
following command:

bash~% ps -o ppid,pid,cmd f | nl | nl
1 1 PPID PID CMD
2 2 6988 21553 -bash
3 3 21553 3185 \_ ps -o ppid,pid,cmd f
4 4 21553 3186 \_ nl
5 5 21553 3187 \_ nl
6 6 6988 6989 -bash

the result seems to show only 3 forked children not 5? anything
wrong?? or 'ps' just hides some information?

Regards,
lihao

Cyrus Kriticos

unread,
Jan 4, 2008, 6:15:11 PM1/4/08
to
liha...@gmail.com wrote:
>
> under my bash 3.1.17(Ubuntu Linux 2.6.18-4-amd64), if I issue the
> following command:
>
> bash~% ps -o ppid,pid,cmd f | nl | nl
> 1 1 PPID PID CMD
> 2 2 6988 21553 -bash
> 3 3 21553 3185 \_ ps -o ppid,pid,cmd f
> 4 4 21553 3186 \_ nl
> 5 5 21553 3187 \_ nl
> 6 6 6988 6989 -bash
>
> the result seems to show only 3 forked children not 5? anything
> wrong?? or 'ps' just hides some information?

ps # child 1 with args: -o ppid,pid,cmd f
nl # child 2
nl # child 3

Why do you want to see 5?

--
Best regards | Be nice to America or they'll bring democracy to
Cyrus | your country.

liha...@gmail.com

unread,
Jan 4, 2008, 6:22:32 PM1/4/08
to
On Jan 4, 6:15 pm, Cyrus Kriticos <cyrus.kriti...@googlemail.com>
wrote:

Just noticed when lcarus said "set up the pipe", he probably meant the
assignment

num=`...`

not the pipe '|'.... my bad:-)

lihao

Dan Stromberg

unread,
Jan 26, 2008, 6:49:52 PM1/26/08
to
On Thu, 03 Jan 2008 22:34:39 -0800, mik3l3374 wrote:

This should be pretty fast and relatively simple too:

(yes '#' | sed -n '1,60p;61q' | tr -d '\012'; echo)

You could also use dd instead of sed, but then you need to 2> /dev/null
to avoid the block counts going to stderr.

If you know your number will always be divisible by 5, for example, you
could use:

(yes '#####' | sed -n '1,12p;13q' | tr -d '\012'; echo)

...but yes is probably using stdio anyway, so it won't really change the
number of systems calls, and the number of execs is the same anyway -
execs usually being the thing that slows down shell scripts the most. It
should be a little easier on the CPU though.

Janis Papanagnou

unread,
Jan 26, 2008, 8:00:03 PM1/26/08
to

If you really care about system calls you may want to avoid processes
and use only bash builtins instead, as in

x=$(printf "%*s" 20 ""); echo "${x// /#}"


Janis

Stephane Chazelas

unread,
Jan 27, 2008, 3:37:24 PM1/27/08
to
On Sun, 27 Jan 2008 02:00:03 +0100, Janis Papanagnou wrote:
[...]

> If you really care about system calls you may want to avoid processes
> and use only bash builtins instead, as in
>
> x=$(printf "%*s" 20 ""); echo "${x// /#}"

$(...) implies a fork(2) and a pipe(2) in every shell but ksh93.

if you really want to avoid syscalls, which is a very strange
thing to be said in shell context, you'd have to do:

i=0 x=
while [ "$i" -lt 20 ]; do
x=#$x
i=$(($i + 1))
done

In zsh:

x=${(l:20::#:)=}

(l:<n>::<pad>:) is a left padding expansion flagged, here
applied to nothing assigned to nothing.

Or:

x=; repeat 20 x+='#'

None of ${x//...}, (l...), repeat or += is standard sh.

--
Stephane

0 new messages