I can find many questions with answers in the other direction, but unfortunately not in the one I would like to have my replacements: I intend to replace a char, such as #, in a string, such as test#asdf, with a sequence, such as {0..10} to get a sequence of strings, in this example test0asdf test1asdf test2asdf test3asdf test4asdf test5asdf test6asdf test7asdf test8asdf test9asdf test10asdf.

I tried, from others:

  • echo '_#.test' | tr # {0..10} (throws usage)
  • echo '_#.test' | sed -r 's/#/{0..10}/g' (returns _{0..10}.test)
  • echo '_#.test' | sed -r 's/#/'{0..10}'/g' (works for the first, afterwards I get sed: can't read (...) no such file or directory)

What is a working approach to this problem?

Edit, as I may not comment yet: I have to use # in the string, in which this character should be replaced, as the string passed from another program. I could first replace it with another char though.

  • Why not just echo _{0..10}.test? The way you have it, it's echoing the string first and then replacing the character which isn't going to work. You could also replace the character and then echo. Aug 27, 2018 at 19:02

5 Answers 5


The {0..10} zsh operator (now also supported by a few other shells including bash) is just another form of csh-style brace expansion.

It is expanded by the shell before calling the command. The command doesn't see those {0..10}.

With tr '#' {0..10} (quoting that # as otherwise it's parsed by the shell as the start of a comment), tr ends being called with ("tr", "#", "0", "1", ..., "10") as arguments and tr doesn't expect that many arguments.

Here, you'd want:

echo '_'{0..10}'.test' 

for echo to be passed "_0.test", "_1.test", ..., "_10.test" as arguments.

Or if you wanted that # to be translated into that {0..10} operator, transform it into shell code to be evaluated:

eval "$(echo 'echo _#.test' | sed 's/#/{0..10}/')"

where eval is being passed echo _{0..10}.test as arguments.

(not that I would recommend doing anything like that).


You can split the string on the delimiter, capture the prefix and the suffix, then use brace expansion to generate the names:

IFS='#' read -r prefix suffix <<<"$str"
names=( "$prefix"{0..10}"$suffix" )
declare -p names
declare -a names='([0]="test0asdf" [1]="test1asdf" [2]="test2asdf" [3]="test3asdf" [4]="test4asdf" [5]="test5asdf" [6]="test6asdf" [7]="test7asdf" [8]="test8asdf" [9]="test9asdf" [10]="test10asdf")'

Do you have to use #? Maybe you could use %d?

$ for i in {1..10}; do printf "_%d.test " "$i"; done
_1.test _2.test _3.test _4.test _5.test _6.test _7.test _8.test _9.test _10.test

First # starts a comment. So, you need to escape it with \.

Second, use a for loop.

Here is your solution:

for i in {1..10}
    echo '_#.test' | tr \# $i

tr unfortunately does not work for more than one character, such as when you want to substitute # with 10. You are better off using sed for that reason.

for i in {1..10}
    echo '_#.test' | sed "s/#/$i/"
  • 1
    What about the "10" result?
    – RudiC
    Aug 28, 2018 at 8:02

I would do it with parameter expansion:

$ var='test#asdf'
$ for i in {1..10}; do echo "${var/\#/"$i"}"; done

The ${parameter/pattern/string} expansion

  • takes the expansion of $parameter (in our case, $var) and
  • replaces the first occurrence of pattern (the escaped #/# has a special meaning in the context, "replace at the beginning of the string", which we want to avoid) with
  • string ("$i" in our case)

Alternatively, you could replace the # with %d and use it as the format string for printf:

printf "${var/\#/%d}\\n" {1..10}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.