1

I need to find a sed/awk command which will insert a character at the nth position of a matching string HAVING LENGTH 10. For example, I need to search for the string starting with pattern 541 and having length 10, then insert 9 at the 5th position of that string without replacement, resulting in an 11 character string.

The strings are words separated by white space. Also the pattern will always be at the beginning of the word but there might be multiple matches per line.

Input file:

5414444444 87654873234  88888888888
6646666666 54122222222 
54155555558888 54176543235 5416666666

Output file:

54149444444 87654873234  88888888888
6646666666 54122222222 
54155555558888 54176543235 54169666666
  • 1
    Please edit your question and clarify. How are "strings" defined? Do you mean "words", strings separated by whitespace? Will the pattern always be at the beginning of the word? Can there be multiple matches per line? – terdon Jul 21 '16 at 9:42
2

As see from example OP mean a word not a string so

sed 's/\b541./&9/g' file

If the 541 can start from elsewhere in word (not from the beginning)

sed 's/\b\S*541/\n&/g     #mark a beginning of word(s) with pattern
     s/\n\(....\)/\19/g   #remove mark and do adding
    ' file

You can limit symbols quantity the word(s) as follows

sed 's/\b\(541.\)\(\S\{6\}\)\b/\19\2/g' file

or more general

sed 's/\b541./&\n/g;s/\n\S\{6\}\b/9&/g;s/\n//g' file
  • Why do you have brackets round the 1? – 123 Jul 21 '16 at 10:01
  • @123 I had afraid \19 would be treated as 19th reverse-match but void – Costas Jul 21 '16 at 10:05
  • @Costas Sed only has 9 backrefs(10 is you count \0, so does perl if you use \n as well. – 123 Jul 21 '16 at 10:07
1

You could use perl

perl -lane 'map{length==10&&/^541/&&s/.{4}/$&9/}@F;print join(" ",@F)' file

Use map to perform a checks and sub on each field. Then prints the array of fields join by a single space(so will mess up formatting if not consistent single spaces between fields)

or just using regex

perl -lane 'map{s/^541.\K.{6}$/9$&/}@F;print join(" ",@F)' file

Output:

54149444444 87654873234 88888888888
6646666666 54122222222
54155555558888 54176543235 54169666666
1

Portably:

sed '
  s/.*/ & /; # add a leading and trailing space
  :1
    s/\([[:blank:]]541[^[:blank:]]\{2\}\)\([^[:blank:]]\{5\}[[:blank:]]\)/\19\2/g
    # replace in a loop until there is no more match
  t1
  # remove the blanks we added earlier:
  s/^ //;s/ $//'

You can avoid the temporary adding of the leading and trailing spaces by looking for the occurrence of that 10 non-blank string at the beginning or the end of the list in addition to following/preceding a blank. That can be done POSIXly with one regexp, but that's a bit unwieldy:

sed '
  :1
    s/^\(\(.*[[:blank:]]\)\{0,1\}541[^[:blank:]]\{2\}\)\([^[:blank:]]\{5\}\([[:blank:]].*\)\{0,1\}\)$/\19\3/
  t1'

With perl, using look-around operators:

perl -lpe 's/((?<!\H)541\H\H)(\H{5})(?!\H)/${1}9$2/g'

Or processing words one by-one:

perl -lpe 's{\H+}{$&=~s/^541..\K.{5}$/9$&/r}ge'

(\K and the r substitution flag require a relatively recent version of perl).

  • Is it posixly \(^\|[[:blank:]]\)? – Costas Jul 21 '16 at 11:05
  • @Costas, no. POSIX BRE have no alternation operator. You can do ^\(.*[[:blank:]]\)\{0,1\}... but that's starting to be a bit too unwieldy. – Stéphane Chazelas Jul 21 '16 at 12:25
  • @StéphaneChazelas: Maybe s/.*/ & /; should be replaced with s/^/ /;s/$/ / to prevent failing when attempt invalid characters. – cuonglm Jul 25 '16 at 16:22

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.