Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I was hoping for a way to make sed replace the entire line with the replacement (rather than just the match) so I could do something like this:

sed -e "/$some_complex_regex_with_a_backref/\1/"

and have it only print the back-reference.

From this question, it seems like the way to do it is mess around with the regex to match the entire line, or use some other tool (like perl). Simply changing the regex to .*regex.* doesn't always work (as mentioned in that question). For example:

$ echo $regex
\([:alpha:]*\)day

$ echo $phrase
it is Saturday tomorrow

$ echo $phrase | sed "s/$regex/\1/"
it is Satur tomorrow

$ echo $phrase | sed "s/.*$regex.*/\1/"

$ # what I'd like to have happen
$ echo $phrase | [[[some command or string of commands]]]
Satur

I'm looking for the most concise way to do this assuming the following:

  • The regex is in a variable, so can't be changed on a case by case basis.
  • I'd like to do this without using perl or other beefier languages.
share|improve this question

4 Answers 4

I don't know sed well enough to answer, but if you are flexible and use grep:

grep --only-matching "complex_regex" file

or

grep -o "complex_regex" file

The --only-matching (or the short form -o) flag tells grep to print out just the matched part, not the whole line.

share|improve this answer
    
This looks like it would be useful in cases where ignoring non-matching lines is ok. After the grep -o, a sed "s/$regex/\1/" would give me what I want. It's not a drop-in replacement for the fictitious sed command that replaces the entire line with the replacement text, and leaves non-matching lines alone. –  jakesandlund Mar 17 '12 at 0:47

Your first .* is stopping at "day", leaving your backreference empty. You need something definite to match against before your [[:alpha:]] in your backreference. e.g. a space,

$ echo $regex
\([[:alpha:]]*\)day

$ echo $phrase
it is Saturday tomorrow

$ echo $phrase | sed "s/.* $regex.*/\1/"
Satur

I love and hate regexes.


edit:

The word boundary non-POSIX extension (\b) seems to catch both cases:

$ regex="\b\([[:alpha:]]\+\)day\b"

I'm not sure how to deal with the situation where the pattern appears multiple times or if there are multiple words in your pattern.

$ cat phrase.txt
it is Saturday tomorrow
it is   Saturday tomorrow
Saturday is the date tomorrow
        Saturday is the date tomorrow
Saturday is the day tomorrow
        Saturday is the day tomorrow
Saturday is the day in dayton tomorrow
        Saturday is the day in dayton tomorrow
Saturday is the day after Friday
The last day of the week is Friday

$ cat phrase.txt | sed -e "s/.*$regex.*/\1/"
Satur
Satur
Satur
Satur
Satur
Satur
Satur
Satur
Satur
Fri

I'm curious if somebody who's got more sed-fu gives a better answer. :-)

share|improve this answer
    
This works in this case, yes. However, I'm looking for a programmatic way to do this that works on any $regex variable. If, for example, $regex was ^something, that space before it would make it fail to match anything. –  jakesandlund Mar 17 '12 at 0:41
    
See the edit... I added some stuff about \b . –  mgjk Mar 17 '12 at 3:08

This is close to mgjk's answer, but with a slightly different approach to boundary matching.

echo $phrase | sed 's/.*[^[:alpha:]]\([[:alpha:]]*\)day.*/\1/'
Satur

Since .* will swallow anything, you'll have to first match "not the character I want" and then "the character I want". So, in $regex you could store

[^[:alpha:]]\([[:alpha:]]*\)day

It is not without quirks (does not work in its currect form if "Saturday" is first on the line), but if you are set on using just sed instead of more potent tools, then it might suffice for you. You could also do it with a two part regex to solve the "beginning of line" problem, but then it is starting to get more complex again, which you do not want. If your criteria change, many solutions exist.

share|improve this answer
up vote 0 down vote accepted

I asked this question on SO as well, and got this answer from potong that does what I was looking for.

sed '/'"$regex"'/!b;s//\n\1\n/;s/.*\n\(.*\)\n.*/\1/' file

Take note that it doesn't depend on knowledge of what's in $regex to work. It uses newlines as a sentinel value in order to later replace the entire line with just the back-reference.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.