I'm trying to find a pattern using sed command in file.txt between first char1 and char2 and then replace that with string. like below with echo mode example:

echo "This X is test Y. But X is not test Y." | sed 's/X[^Y]*Y/REPLACE/'

Also I need to save matched pattern( like is test {<--spaces around is important} ) in a variable.

  • 1
    In a shell variable? Because you can use backreferences in sed: sed 's/X\([^Y]*\)Y/\1/' - the \1 will contain everything matched by [^Y]*. – muru Oct 24 '14 at 6:41
  • @muru I've also tried that, correct, but I need to replace that with new word and save into a variable like "VAR" – αғsнιη Oct 24 '14 at 6:47

Here is a single invocation of sed that writes the revised line to stdout while at the same time saving the removed text in shell variable var:

$ var=$(echo "This X is test Y. But X is not test Y." | sed -nr 'h;s/[^X]*X([^Y]*)Y.*/\1/;p;x;s/X[^Y]*Y/REPLACE/;w /dev/stderr') 2>&1
This REPLACE. But X is not test Y.

The value of var is:

$ echo "==$var=="
== is test ==


  • h

    This command copies the current pattern to the hold space.

  • s/[^X]*X([^Y]*)Y.*/\1/;p

    This removes everything from the pattern space except the text between the first X and Y including any spaces. This is then printed to stdout. This is the output that is captured by the shell and assigned to var.

  • x

    This copies the hold space back to the pattern space. When this is done, the pattern space contains a copy of the original input line.

  • s/X[^Y]*Y/REPLACE/; w /dev/stderr

    The substitution is made and the result is written to stderr.

  • 2>&1

    After the shell has captured stdout into var, this instructs the shell to copy stderr (which has the line with REPLACE) to stdout.

Aside on handling of variable var

The variable var includes the leading and trailing spaces. If the shell were to subsequently subject var to word-splitting, these spaces would be removed. To prevent that, when var is referenced, do so inside double-quotes, as in the example above.


You have to accomplish that with a second sed command:

var=$(echo "This X is test Y. But X is not test Y." | \
tee >(sed 's/X[^Y]*Y/REPLACE/' >/dev/stderr) | \
sed -r 's/[^X]*X([^Y]*)Y.*/\1/')


  • You have to use tee to split the stream. That both stdout and stderr contain the string.
  • tee needs a file as argument. We give tee a pipe where sed listens from and replaces the string.
  • sed prints the string to stderr: The output you see in the terminal.
  • The second sed command listens on stdout and extracts the needed string, that is now saved into the variable $var (the contect of stdout).

Use that to check:

$ echo ">$var<"
> is test <

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.