2

I can replace only the 5th and 7th spaces with the command:

$ echo "1 2 3 4 5 6 7 8" | sed 's/\ /x/5;s/\ /x/6'
1 2 3 4 5x6 7x8

but does maybe sed support lists and ranges of nth pattern occurrences to use just one substitute s/ instead of the two separated by semicolons?

$ echo "1 2 3 4 5 6 7 8" | sed 's/\ /x/5,7'
sed: -e expression #1, char 9: unknown option to `s'
  • only range of line numbers – RomanPerekhrest Mar 22 '18 at 16:07
  • 3
    If you do the 7th and then the 5th, you don't have to adjust the numbers. – Wildcard Mar 22 '18 at 21:27
2

With shells with csh-like brace expansion (csh, tcsh, zsh, bash, pdksh, ksh93, yash -o braceexpand, fish), you could do:

$ echo 1 2 3 4 5 6 7 8 9 | sed '-es/ /x/'{7,6,5}
1 2 3 4 5x6x7x8 9 

Brace expansion expands that to

sed '-es/ /x/'7 '-es/ /x/'6 '-es/ /x/'5

We're doing it backwards so that one substitution has no influence on the next one.

You can make it a range with the {x..y} form from zsh (now also supported by bash, ksh93, yash -o braceexpand)

$ echo 1 2 3 4 5 6 7 8 9 | sed '-es/ /x/'{7..3}
1 2 3x4x5x6x7x8 9

or combinations of both forms:

$ echo 1 2 3 4 5 6 7 8 9 | sed '-es/ /x/'{{7..3},1}
1x2 3x4x5x6x7x8 9

With zsh, you can put those in an array:

$ substs=({7..5} 3 1)
$ echo 1 2 3 4 5 6 7 8 9 | sed '-es/ /x/'$^substs
1x2 3x4 5x6x7x8 9

And have zsh sort them in the correct reverse numerical order:

$ substs=(1 8 6)
$ echo 1 2 3 4 5 6 7 8 9 | sed '-es/ /x/'${(On)^substs}
1x2 3 4 5 6x7 8x9

With zsh or bash, you can also do:

$ substs=({7..5} 3 1)
$ echo 1 2 3 4 5 6 7 8 9 | sed "${substs[@]/#/-es: :x:}"
1x2 3x4 5x6x7x8 9

With fish (and GNU seq), you can do things like:

$ echo 1 2 3 4 5 6 7 8 9 | sed '-es/ /x/'(seq 6 -1 3)
1 2 3x4x5x6x7 8 9

Or:

$ set substs 5 3 1
$ echo 1 2 3 4 5 6 7 8 9 | sed '-es/ /x/'$substs
1x2 3x4 5x6 7 8 9

(fish arrays expand like in rc or zsh -o rcexpandparam in this instance).


With perl, you could do something like:

$ echo 1 2 3 4 5 6 7 8 9 | perl -pe 'BEGIN{$h{$_}=1 for (3,5..8)}
                                     my $n; s/ /$h{++$n} ? "x" : $&/ge'
1 2 3x4 5x6x7x8x9
  • Wooow!, this is really interesting, I didn't know with brace expansion can do such a thing like you did! – αғsнιη Mar 23 '18 at 6:36
  • I found that the range should be always {max#..min#} as well as numbers range in {x,y,z} shold be x>y>z and {x,x} if same numbers will cause a problem (BTW for this there is s/ /x/X alternative where X is position number). – αғsнιη Mar 23 '18 at 6:57
  • I posted an answer to a question on AU with your idea. – αғsнιη Mar 23 '18 at 7:12
0
$ echo $SHELL
/bin/bash
$ set -- 6 7 # or bash -s 6 7 (set positional parameters)
$ echo "1 2 3 4 5 6 7 8" | sed "s/ /x/$1"
1 2 3 4 5 6x7 8
$ shift # ¹
$ echo "1 2 3 4 5 6 7 8" | sed "s/ /x/$1"
1 2 3 4 5 6 7x8

¹ :

$ help shift
shift: shift [n]
Shift positional parameters.

Rename the positional parameters $N+1,$N+2 ... to $1,$2 ... If N is not given, it is assumed to be 1.

Exit Status: Returns success unless N is negative or greater than $#

  • Why create array arr at all? Why not just set -- 6 7? – user1404316 Mar 23 '18 at 1:56
  • Improved snippet, thanks and added explanations – Gilles Quenot Mar 23 '18 at 5:41
0

You can achieve this with GNU sed as following considering that the range you want to replace is 5~12 (inclusive) as an example and assuming there is no # occurred in your file at all (or use another character instead of # that it's guarantee it will not occur), then you can do:

sed 's/ /#/13g; s/ /x/5g ; s/#/ /g' infile
  • s/ /#/13g, replaces all spaces at 13th position to the end with #
  • s/ /x/5g, replaces all spaces at position 5th to the end (which will until 12th position) with x.
  • s/#/ /g, revert back all spaces.

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.