2

I need to find all occurrences of AAsomeArbitraryStringBB and replace it with CCsomeArbitraryStringDD.

So

AAHelloBB
Text
AAByeByeBB

becomes

CCHelloDD
Text
CCByeByeDD.

It's important to note that the replacement string contains part of the search string.

  • 2
    There is no such thing as a "native Linux command". Linux is a kernel. What's on top if it can differ between various systems. And even if two Linux based systems have the same program installed, these programs can be completely different, can be written in different languages, have different documentation. – Arkadiusz Drabczyk Jan 12 '15 at 19:33
2

This is basic task for sed command:

sed 's/AA\(someArbitraryString\)BB/CC\1DD/g'

Eventually if you want to do this for all "arbitrary strings":

sed 's/AA\(.*\)BB/CC\1DD/g'
  • thanks. the second command was the solution to my problem – User133713 Jan 12 '15 at 19:43
  • Suppose arbitrary strings should be [:alnum:] – Costas Jan 12 '15 at 19:44
  • @mikeserv yes for future readers, if you don't mind. It looks quite complicated. – jimmij Jan 12 '15 at 23:34
  • ok, jimmij, I think that does. While tooling it over I realized I could simplify it in a big way - it doesn't need to nest any sub-expressions any more. – mikeserv Jan 13 '15 at 2:16
2
sed "s/\([AB]\)\1\1*/\n&\n/g
     s/AA\n\([[:alnum:]]\{1,\}\)\nBB/CC\1DD/g
     s/\n//g
" <<\INPUT
AAHelloBB Text AAByeByeBB
INPUT

I think that should only do the AA>>CC&&BB>>DD replacement if there is 1 or more alphanumeric character[s] between the two groups and should always squeeze the possible occurrences as near to each other as it might.

The example prints:

CCHelloDD Text CCByeByeDD

The hard part of this is done is in the first s///ubstitution statement. It places a \newline character at the head and tail of all AAA* and BBB* occurrences in pattern space simultaneously. Delimiting can be tricky sometimes - often placing a head-end delimiter can change where a tail-end delimiter should go or vice-versa. I try to take that step in a single bound whenever possible to avoid having to edit an edit.

So we'll look at it inside out, but as we do consider that sed is scanning pattern space from left-to-right for every occurrence of the resultant pattern because I tack the global flag onto the tail of s///ubstitution statement.

  • [AB] - sed will pause its scan when encountering the first A or B in any series of either as it scans. It will next look for...
  • \([AB]\)\1\1* - at least one immediately subsequent identical character and any/all continuing identical characters for as long as the sequence can last. I group the [AB] character class in a \(sub-expression\) and so can refer to its contents with the back-reference \1.
    • This is different than doing [AB]\{2,\} or even \([AB]\)\{2,\} as in those cases sed will consider both A and B to match the pattern. Rather here all characters added to the match group are identical to the first match on [AB].
  • \n&\n - in the right-hand-side of the s///ubstitution I reference the entire sequence just matched with & and insert at its head and tail a \newline character.
    • Many seds will not support a \newline backslash escape in the right-hand-side of a substitution. If this is the case for you you can simply use a literal \newline character in place the n instead.

Here is a look at the results of this s///ubstitution on the example input string:

\nAA\nHello\nBB\n Text \nAA\nByeBye\nBB\n

You can see that other than inserting extra \newline characters (which is pretty much the only character that can only occur in a sed pattern space as a result of an edit) sed has not altered the string at all - no input character is modified.

You can also see that every AAA* or BBB*sequence is now immediately enclosed within \newlines. So when I do the next global s///ubstitution I just have to tell sed to...

  • AA\n - begin each match only at the tail end of a AAA* sequence immediately followed by...
  • \([[:alnum:]]\{1,\}\) - one or more alphanumeric characters. This should never work out to be a BBB* sequence because where AAA* immediately precedes BBB* there are now two intermediate \newlines betwixt them. This alphanumeric sequence must be immediately followed by...
  • \nBB - the head end of a BBB* sequence.

And in the right-hand-side...

  • CC\1DD - we replace AA\n w/ CC \1 with itself and \nBB w/ DD.

At this point a look reveals...

\nCCHelloDD\n Text \nCCByeByeDD\n

...apparent success! We now need only to do...

`s/\n//g`

...and remove any remaining \newline delimiters and the work is done.

Here's the result of my hammering randomly at the keyboard for a seconds rendered as input. It is a far more complicated input example so I split it out with escaped \newlines in a here-document. The shell will remove all the newlines you can see here before passing the single line result to sed as input:

sed ... <<IN
AA  kj \
BB\
AAAAAABAkl\
AAAAasjd\
AAAAfo\
BB\
AAia\
BBsdfjomAl\
BBks\
BBmdlmdsviom\
BB\
AAiodsvgmnoi
IN

...and a look following the first s///ubstitution:

\nAA\n kj \nBB\n\nAAAAAA\nBAkl\nAAAA\nasjd\nAAAA\nfo\nBB\n\nAA\nia\nBB\nsdfjomAl\nBB\nks\nBB\nmdlmdsviom\nBB\n\nAA\niodsvgmnoi

...and following the second...

\nAA\n kj \nBB\n\nAAAAAA\nBAkl\nAAAA\nasjd\nAACCfoDD\n\nCCiaDD\nsdfjomAl\nBB\nks\nBB\nmdlmdsviom\nBB\n\nAA\niodsvgmnoi

...and the final product...

AA kj BBAAAAAABAklAAAAasjdAACCfoDDCCiaDDsdfjomAlBBksBBmdlmdsviomBBAAiodsvgmnoi

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.