Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I'm trying to write sed expression which will extract matching groups from file.

E.g. I have .cue sheet and I want to extract it's genre tag. It has following format:

$ cat file.cue | grep GENRE
REM GENRE "POP"

I've created sed expression to extract this tag and replace quotes around it:

$  sed -e 's/REM[ \t]*GENRE[ \t]*\(.*\)/\1/;s/"\(.*\)"/\1/;q' file.cue 
POP

My question is why if it's failed to find matching group it will just print first string?

$  sed -e 's/REMerror[ \t]*GENRE[ \t]*\(.*\)/\1/;s/"\(.*\)"/\1/;q' file.cue 
REM GENRE POP

Example of cue-sheet:

REM GENRE "POP"
REM DATE 2012
REM DISCID EC10BD10
REM COMMENT "ExactAudioCopy v0.99pb5"
PERFORMER "JAM Project"
TITLE "JAM Project BEST COLLECTION IX THE MONSTERS"
FILE "LACA-15250.flac" WAVE
  TRACK 01 AUDIO
    TITLE "THE MONSTERS"
    PERFORMER "JAM Project"
    INDEX 01 00:00:00
share|improve this question
    
@Braiam, I've added example of cue-sheet. –  diffycat Jan 19 at 6:52

2 Answers 2

up vote 4 down vote accepted

Your sed script, as it stands, will always print the first line regardless of whether it was modified. The default action for sed is to print its pattern space (current input line1) to standard out after it's done applying all your other commands.

So, basically, what you're telling it to do is to apply a few substitutions and then quit. What this means is that it will try your substitutions on the first line and print it after the substitutions (regardless of whether they were successful) since this is the default action. It then encounters q and quits processing altogether.

If you want to suppress the default printing action, you need the -n switch, but then you need to explicitly instruct sed to print the lines where substitution was successful:

sed -n 's/REM[ \t]*GENRE[ \t]*"\(.*\)"/\1/p;q' your_file_here

Now this script will print the first line where the substitution was successful and then quit.

Note that you didn't need two substitutions, only one is enough.

Unrelated side note

No need for

cat your_file | grep 'foobar'

when

grep 'foobar' your_file

would do.


1Not always the current input line: you may for example add lines to the pattern space.

share|improve this answer

If you're not opposed to using grep instead, and you grep so happens to be the GNU implementation built with PCRE support, you can use its PCRE facilities (Perl Compatible Regular Expressions).

$ grep -oP '.*(?<=GENRE ").*(?=")' file.cue 
POP

The above looks for the string ...GENRE " on the right of .* followed by a " on the right. You can tighten up the .* to be \w+ if they're all word characters for example.

What was wrong with sed?

The issue is you had 2 search and replaces in your example.

  • s/REMerror[ \t]*GENRE[ \t]*\(.*\)/\1/;
  • s/"\(.*\)"/\1/;

The first doesn't match anything and does nothing. The second one matches the double quoted word "POP" and returns the contents, POP, forming the string you see,

REM GENRE POP

You can convince yourself that that's what's going on by taking out the first search.

$ sed -e 's/"\(.*\)"/\1/;q' file.cue 
REM GENRE POP
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.