12

I saw from

How to use sed to replace only the first occurrence in a file?

How to do most of what I want:

sed -n '0,/.*\(something\).*/s//\1/p'

This finds the first match of something and extracts it (of course my real example is more complicated). I know sed has a 'quit' command which will make it stop early, but I don't know how to combine the 'q' with the above line to get my desired behavior.

I've tried replacing the 'p' with {p;q;} as I've seen in some examples, but that is clearly not working.

Community
  • 1
  • 1
nosatalian
  • 6,889
  • 4
  • 20
  • 16
  • Can you show more of what you are getting and what you want and how they differ? I did `echo -e "aaa 111 aaa\nbbb 111 bbb"|sed -n '0,/.*\(111\).*/s//\1/p'` and got one "111". Then I did `echo -e "aaa 111 aaa\nbbb 111 bbb"|sed -n '0,/\(.*\(111\).*\)/s//\1/p'` to show the full line to make sure what it was getting and it output "aaa 111 aaa" which is what I expected. Notice that I'm not using a `q`. How is it "not working" for you. – Dennis Williamson Nov 21 '09 at 03:48
  • Your method generates the correct output, but it continues processing the entire file even though there is no more work to do, which is awful for giant files or when used in a pipeline. My specific use case was to find out via 'git log' on a git-p4 imported tree which perforce branch was used for the last commit. git log (when called without -n will log every commit that every happened (hundreds of thousands for me). We can't know a-priori what value to give git for '-n.' My solution was: git log | sed -n '0,/.*\[git-p4:.*\/\/depot\/blah\/\([^\/]*\)\/.*/s//\1/p; /\[git-p4/ q' – nosatalian Nov 24 '09 at 23:50

5 Answers5

12

The "print and quit" trick works, if you also put the substitution command into the block (instead of only putting the print and quit there).

Try:

sed -n '/.*\(something\).*/{s//\1/p;q}'

Read this like: Match for the pattern given between the slashes. If this pattern is found, execute the actions specified in the block: Replace, print and exit. Typically, match- and substitution-patterns are equal, but this is not required, so the 's' command could also contain a different RE.

Actually, this is quite similar to what ghostdog74 answered for gawk.

Community
  • 1
  • 1
King Thrushbeard
  • 869
  • 10
  • 16
  • 2
    unfortunately, gives an error on macOS `extra characters at the end of q command`. The solution is easy: put a semicolon after q: `{s//\1/p;q;}` – ReDetection Feb 12 '21 at 03:37
  • This should be the accepted answer for the question OP asked. – ryenus Nov 09 '22 at 07:28
2

My specific use case was to find out via 'git log' on a git-p4 imported tree which perforce branch was used for the last commit. git log (when called without -n will log every commit that every happened (hundreds of thousands for me)).

We can't know a-priori what value to give git for '-n.' After posting this, I found my solution which was:

git log | sed -n '0,/.*\[git-p4:.*\/\/depot\/blah\/\([^\/]*\)\/.*/s//\1/p; /\[git-p4/ q' 

I'd still like to know how to do this with a non '/' separator, and without having to specify the 'git-p4' part twice, once for the extraction and once for the quit. There has got to be a way to combine both on the same line...

nosatalian
  • 6,889
  • 4
  • 20
  • 16
1

Sed usually has an option to specify more than one pattern to execute (IIRC, it's the -e option). That way, you can specify a second pattern that quits after the first line.

Another approach is to use sed to extract the first line (sed '1q'), then pipe that to a second sed command (what you show above).

David R Tribble
  • 11,918
  • 5
  • 42
  • 52
1

use gawk

gawk '/MATCH/{
            print "do something with "$0
            exit 
}' file
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
0

To "return something from first line and quit" you could also use grep:

grep --max-count 1 'something'

grep is supposed to be faster than sed and awk [1]. Even if not, this syntax is easier to remember (and to type: grep -m1 'something').

If you don't want to see the whole line but just the matching part, the --only-matching (-o) option might suffice.

Alp
  • 396
  • 3
  • 6