8

I'm using | sed to indent the result of some command (let's take a plain $ echo something for this example). I want to prefix the result with, say, 10 spaces. The following works fine:

$ echo something | sed 's/^/         /'
         something

But isn't there a way to use quantifiers in the | sed expression?

I tried for example

$ echo something | sed 's/^/ {10}/'
 {10}something

But obviously it doesn't work. {10} isn't interpreted as a quantifier.

And protecting the braces with backslashes doesn't work either:

$ echo something | sed 's/^/ \{10\}/'
 {10}something

Is there a way to use quantifiers in the substitution expression?

4
  • 2
    You could use awk instead ... | awk '{printf "%10s%s\n", "", $0}'. Know you are asking for sed, so only a comment.
    – ibuprofen
    Jul 15, 2021 at 9:51
  • 2
    @ibuprofen : that's a great and short solution, I usually don't think at awk when I think "string replacement", but it seems awk can sometimes be more convenient than sed. Thanks
    – ChennyStar
    Jul 15, 2021 at 10:04
  • 2
    Yeah printf in general is a powerful tool for formatting. Have a look into it if not familiar with it. It differs some between implementations, i.e. shells, coreutils, c, awk, perl, ... but basics are the same. E.g. also: awk -v width=10 '{printf "%*s%s\n", width, "", $0}' (using asterisk in format string to denote width is a variable).
    – ibuprofen
    Jul 15, 2021 at 10:31
  • 4
    For this particular case, you could insert a single tab character, then "multiply" it to a desired number of space characters using the expand command: echo 'something' | sed 's/^/\t/' | expand -it 10 Jul 15, 2021 at 14:39

7 Answers 7

12

I don't think it's possible since the replacement in sed substitutions isn't a regular expression.

An alternative could be to create a variable and use it as replacement, like this:

# use printf to set as many spaces as you want
$ i=$(printf "%10s")

# use the created variable as replacement of the substitution
$ echo something | sed "s/^/$i/"
          something
3
  • 1
    @steeldriver I was jsut reading the sed manual and was wondering if that would be an option :) Jul 15, 2021 at 11:53
  • 1
    @schrodigerscatcuriosity : "I don't think it's possible since the replacement in sed substitutions isn't a regular expression". YES, it is possible, without using printf. Check my solution below (echo something | sed ':lbl; /^ \{10\}/! {s/^/ /;b lbl}')
    – ChennyStar
    Jul 15, 2021 at 16:16
  • 2
    Note that $(printf "%10s") really prints a space-padded empty string; an alternate that is closer to the concept of a "quantifier" might be $(printf " %.0s" $(seq 10)) which could be applied to any substring $(printf "abc%.0s" $(seq 10)), similar to perl's x 10 string repetition. Jul 15, 2021 at 17:34
12

A lot of good solutions, I especially like @nezabudka's | pr -T -o10 solution, really short and simple.

Now, for the fun of it, I found a "sed only" solution (no printf, no expand, no nothing) :

$ echo something | sed ':L; /^ \{10\}/! {s/^/ /;b L}'
          something

EDIT: At @Barmar's request, some explanation :

  • :L; creates a label, named L (you could name it anything)
  • We append a space at the beginning of the line, with s/^/ /. Then we go back to the label (b L, we branch to the label L, kind of a goto)
  • And we make this expression conditional : it's only executed as long as the line doesn't start with 10 spaces (/^ \{10\}/!)

So, basically, we repeat "add a space at the beginning of the line" until the line begins with 10 spaces

While @nezabudka's pr solution is clearly the easiest way to append spaces in front of a line, this sed solution is more versatile. To add for example 5 times XYZ- in front of a line :

$ echo something | sed ':L; /^\(XYZ-\)\{5\}/! {s/^/XYZ-/;b L}'
XYZ-XYZ-XYZ-XYZ-XYZ-something
9
  • 1
    Amazing! That's guru level :) Jul 15, 2021 at 17:06
  • If your guru is into hacks. ;) (It's certainly clever...but contrived.)
    – B Layer
    Jul 17, 2021 at 2:22
  • @BLayer I was referencing to the sed's manual Commands for sed gurus ;-) Jul 17, 2021 at 16:54
  • @schrodigerscatcuriosity Ah. I know that documentation but didn't remember that section name. Nice obscure reference. ;)
    – B Layer
    Jul 17, 2021 at 18:12
  • Could you explain how this works for those of us who aren't sed gurus (I've always been of the opinion that anything that requires use of sed's hold space should be done in another language).
    – Barmar
    Jul 22, 2021 at 17:41
11

Some of Perl's best stuff was stolen from sed, so the similarities can be useful. In this case, the perl solution is simpler and perhaps thinking of it in this way may make the sed solution obvious. What you want is to print 10 spaces and then print your output, rather than transforming the line and printing the result.

echo something | perl -pe 'print " " x 10'

The -p flag adds an implicit print $_ to the end of the command.

If you really want to use a regex, use the /e flag

echo something | perl -pe 's/^/" " x 10/e'

which runs the substitution as an executable statement and uses the result as the subbed value. (in this case I get 10 's by using the repetition operator, x )

11

Perhaps the pr utility will suit you for this purpose:

echo $'something\nstuff' | pr -T -o10
          something
          stuff

Don't forget about the -T option. That stops pr from paginating input and writing headers.

1
  • Great solution too ! Never heard of pr before ! Thanks
    – ChennyStar
    Jul 15, 2021 at 10:14
2

You could get your shell to generate the sequence of 10 spaces.

For instance, with zsh:

$ echo something | sed "s/^/${(l[10])}/"
          something

Here using the left padding parameter expansion flag applied to the empty parameter expansion. You could just as well use the right one here. To pad with a different character:

$ echo something | sed "s/^/${(l[10][.])}/"
..........something

Or even sequence of characters:

$ echo something | sed "s/^/${(l[10][<>])}/"
<><><><><>something
2
  • 1
    Doesn't work in bash (bad substitution)
    – ChennyStar
    Jul 23, 2021 at 13:27
  • @ChennyStar, no, as I said, that's for zsh, bash has not equivalent, though you could use printf there as others have shown. Jul 23, 2021 at 13:29
1

Using Raku (formerly known as Perl_6)

raku -pe 's/ ^^ (.*) $$ /{print " " x (10-$0.chars); $0}/;'

Sample Input:

when
in
the
course
of
human
events

Sample Output:

  when
    in
   the
course
    of
 human
events

Nice coding in Perl from @Boyd led me to this answer, which is a simple way to right-justify lines. The Raku s/// operator tolerates extraneous spaces on the left (matcher) side, so I've broken out the match elements: ^^, .*, and $$. Of course, the simpler answer to the question is to delete the -$0.chars section of the code, so everything is indented 10 spaces (after @Boyd):

echo something | raku -pe 'print " " x 10;'

For the code presented at the top, clearly you can change the matcher, and it would be fun to specifically match (before? after?) lines starting with proofreader-marks, such as § or . Finally, Raku is advertised to be Unicode-ready, so there shouldn't be a worry about mis-alignment (although the test below is extremely limited):

raku -pe 's/ ^^ (.*) $$ /{print " " x (4-$0.chars); $0}/;' uni-test.txt
   A
   Á
   Å
   À
   Ä
   B
   ß
   œ
   þ

https://www.raku.org/

0

Another (gnu) sed exclusive solution below: It is possible using the sed GNU extension "e" command thats allows one to pipe a shell command input directly from sed as this example.

Note that the {1..10} expression: I think it is bash extension not sure it will works with other shells.

echo -e "foo\nbar\r" | sed 'e for i in {1..10};do echo -n ".";done'
..........foo 
..........bar

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .