Grep one line linux

How to grep a specific line _and_ the first line of a file?

This provides much information, but as the first line of the ps command is missing there is no context for the info. I would prefer that the first line of ps be shown as well:

$ psa aux | someMagic someApp USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND 1000 11634 51.2 0.1 32824 9112 pts/1 SN+ 13:24 7:49 someApp 
$ ps aux | grep -E "COMMAND|someApp" 

However, I would prefer a more general solution as there are other cases in which I would like to have the first line as well. Seems like this would be a good use case for a «stdmeta» file descriptor.

The complexity required by these answers shows how the Unix philosophy of «do one thing and do it well» sometimes fails us when measured by the yard stick of usability: knowing all these commands well enough to apply them to this common problem (filtering process info and still seeing the column labels) shows the downside of the approach: sometimes things don’t fit together very cleanly. This is why tools like ack are so useful, and why perl rocketed past sed , awk , etc. in popularity: it’s important for the parts to sum up into a coherent whole.

of course, for this particular example, you could use the -C argument to ps and you wouldn’t need to pipe it into grep. e.g. ps u -C someApp or even ps u -C app1 -C app2 -C app3

@iconoclast: of course the Unixy solution would be a tool that can multiplex multiple lines each to be filtered through different set of filters. Kinda a generalized version of ps aux | < head -1; grep foo; >mentioned by @Nahuel Fouilleul below (his is probably the only solution that I’d be able to recall on the spot if needed)

@iconoclast: Lacking experience with, and knowledge of the tools, what the tools really do well will always seem entirely useless. Knowing a command well is no where on the yard stick of usability, it’s on the yard stick of read the fine manual and practice. These tools have been around for decades. They work and fit together very nicely (and cleanly).

@ЯрославРахматуллин: I think you may have completely misunderstood what I said. (Perhaps because English is not your first language?) «Usability» is related to UX («user experience») not utility (or «usefulness»). Pointing out that when a simple operation is this complex it hurts usability is NOT the same as saying the tools are useless. Quite obviously they are not useless. No one in their right mind would say they are useless.

15 Answers 15

Good way

Normally you can’t do this with grep but you can use other tools. AWK was already mentioned but you can also use sed , like this:

How it works:

  1. Sed utility works on each line individually, running specified commands on each of them. You can have multiple commands, specifying several -e options. We can prepend each command with a range parameter that specifies if this command should be applied to specific line or not.
  2. «1p» is a first command. It uses p command which normally prints all the lines. But we prepend it with a numerical value that specifies the range it should be applied to. Here, we use 1 which means first line. If you want to print more lines, you can use x,yp where x is first line to print, y is last line to print. For example to print first 3 lines, you would use 1,3p
  3. Next command is d which normally deletes all the lines from buffer. Before this command we put yourpattern between two / characters. This is the other way (first was to specify which lines as we did with p command) of addressing lines that the command should be running at. This means the command will only work for the lines that match yourpattern . Except, we use ! character before d command which inverts its logic. So now it will remove all the lines that do not match specified pattern.
  4. At the end, sed will print all the lines that are left in buffer. But we removed lines that do not match from the buffer so only matching lines will be printed.
Читайте также:  Как называются диски в linux

To sum up: we print 1st line, then we delete all the lines that do not match our pattern from input. Rest of the lines are printed (so only lines that do match the pattern).

First line problem

As mentioned in comments, there is a problem with this approach. If specified pattern matches also first line, it will be printed twice (once by p command and once because of a match). We can avoid this in two ways:

  1. Adding 1d command after 1p . As I already mentioned, d command deletes lines from buffer and we specify it’s range by number 1, which means it will only delete 1st line. So the command would be sed -e ‘1p’ -e ‘1d’ -e ‘/youpattern/!d’
  2. Using 1b command, instead of 1p . It’s a trick. b command allows us to jump to other command specified by a label (this way some commands can be omitted). But if this label is not specified (as in our example) it just jumps to the end of commands, ignoring rest of the commands for our line. So in our case, last d command won’t remove this line from buffer.

Full example:

ps aux | sed -e '1b' -e '/syslog/!d' 

Using semicolon

Some sed implementations can save you some typing by using semicolon to separate commands instead of using multiple -e options. So if you don’t care about being portable the command would be ps aux | sed ‘1b;/syslog/!d’ . It works at least in GNU sed and busybox implementations.

Crazy way

Here’s, however, rather crazy way to do this with grep. It’s definitely not optimal, I’m posting this just for learning purposes, but you may use it for example, if you don’t have any other tool in your system:

ps aux | grep -n '.*' | grep -e '\(^1:\)\|syslog' 

How it works

  1. First, we use -n option to add line numbers before each line. We want to numerate all the lines we we are matching .* — anything, even empty line. As suggested in comments, we can also match ‘^’, result is the same.
  2. Then we are using extended regular expressions so we can use \| special character which works as OR. So we match if the line starts with 1: (first line) or contains our pattern (in this case its syslog ).
Читайте также:  Разархивировать tar linux команда

Line numbers problem

Now the problem is, we are getting this ugly line numbers in our output. If this is a problem, we can remove them with cut , like this:

ps aux | grep -n '.*' | grep -e '\(^1:\)\|syslog' | cut -d ':' -f2- 

-d option specifies delimiter, -f specifies fields (or columns) we want to print. So we want to cut each lines on every : character and print only 2nd and all subsequent columns. This effectively removes first column with it’s delimiter and this is exactly what we need.

Источник

Grep only the first match and stop

I’m searching a directory recursively using grep with the following arguments hoping to only return the first match. Unfortunately, it returns more than one — in-fact two the last time I looked. It seems like I have too many arguments, especially without getting the desired outcome. :-/

# grep -o -a -m 1 -h -r "Pulsanti Operietur" /path/to/directory 
Pulsanti Operietur Pulsanti Operietur 

7 Answers 7

-m 1 means return the first match in any given file. But it will still continue to search in other files. Also, if there are two or more matched in the same line, all of them will be displayed.

You can use head -1 to solve this problem:

grep -o -a -m 1 -h -r "Pulsanti Operietur" /path/to/dir | head -1 

explanation of each grep option:

-o, --only-matching, print only the matched part of the line (instead of the entire line) -a, --text, process a binary file as if it were text -m 1, --max-count, stop reading a file after 1 matching line -h, --no-filename, suppress the prefixing of file names on output -r, --recursive, read all files under a directory recursively 

I don’t think they are necessary (except for -r obviously), but they should not hurt (I would not use -a though)

Exactly what I needed. My pattern was found twice on the same line and grep -m 1 returned both instances because of this. |head -1 solved it!

@Chris_Rands the exact behavior depends on the shell that you are running in. head will exit as soon as it encounters the first line. grep will exit the next time it tries to write after head has exited. Some shells will wait until all elements of a pipeline finish, some will cause the entire pipe to shutdown as soon as the last program in the pipe exits.

@3Qn, I don’t understand your comment: first not first from result . This answer prints first match in any file and stops. What else did you expect?

You can pipe grep result to head in conjunction with stdbuf.

Note, that in order to ensure stopping after Nth match, you need to using stdbuf to make sure grep don’t buffer its output:

stdbuf -oL grep -rl 'pattern' * | head -n1 stdbuf -oL grep -o -a -m 1 -h -r "Pulsanti Operietur" /path/to/dir | head -n1 stdbuf -oL grep -nH -m 1 -R "django.conf.urls.defaults" * | head -n1 

As soon as head consumes 1 line, it terminated and grep will receive SIGPIPE because it still output something to pipe while head was gone.

This assumed that no file names contain newline.

I’m trying to adopt this solution to search in a large number of archive files with xargs : find . -name ‘*.gz’ | xargs -I ‘<>‘ stdbuf -oL zgrep -al ‘pattern’ <> | head -n 1 . This, however, does not terminate on the first match. Any advice?

Читайте также:  Посмотреть количество соединений linux

Wouldn’t grep ‘s —line-buffered option prevent buffer overhead without calling an additional utility?

For anyone who lands here, as I did, perplexed as to why —max-count didn’t seem to be working when acting on stdin .

TL;DR — —max-count n does NOT stop after finding n matches, it stops after finding all matches on n lines.

(And stdin , even if it’s only a string, counts as one line.)

This is true despite the fact that, in zsh 5.8 , at least, man grep describes the option this way:

-m num, --max-count=num Stop reading the file after num matches. 

Longer Explanation

In my case, I was trying to grab just the first part of a relative path ( some in the example below):

> echo "some/path/here" | grep -E -o -m 1 '[^\/]+' 

and was quite confused when it gave me back

Thanks to the comment from @harperville above, I finally figured out: It’s not about the output, it’s about the input.

> echo "some/path/here\nanother/path/there" | grep -E -o -m 1 '[^\/]+' 

I got the same result as above (i.e., only the parts before the \n in this second example).

For those who are less familiar with grep :

  • -E ( —extended-regexp ) tells it to use «extended» regular expressions, i.e., the ones you’re used to from most other programming languages. The differences between «extended» and «basic» aren’t big — it’s just about which characters you need to escape in your regex — but as someone who’s primarily a TS and Python developer, I always use -E because that way I never have to think about it. (Pro-tip: Add alias grep=»grep -E» to your .zshrc and you’ll never have to worry about it again!)
  • -o ( —only-matching ) tells it to only print the matches, rather than each line on which it found a match.
  • -m n ( —max-count n ) restricts it to searching n lines. (If you’ve read this far you clearly already know that, though! 😛)

Источник

How can I use grep to search only on the first line of files for a specific string?

It’s probably easiest to write a perl 1-liner to do this, otherwise it would have to be a hacky combination of head and grep.

@peterh Rather than asking the question again, it is better to flag the current question for migration there.

10 Answers 10

With awk

or if your awk version doesn’t support nextfile (thanks to Stéphane Chazelas for the suggestion) :

will read only the first line before switching to next file, and print it only if it matches «pattern» .

Advantages are that one can fine-tune both the field on which to search the pattern for (using e.g. $2 to search on the second field only) and the output (e.g. $3 to print the third field, or FILENAME , or even mix).

Note that with the FNR («current input record number», i.e. line number) version you can fine-tune further the line(s) on which you want to grep : FNR==3 for the third line, FNR

With head , keeping filenames

head -n1 -v mydir/files*|grep -B1 pattern 

-v option of head will print filenames, and option -B1 of grep will print the previous line of matching lines — that is, the filenames. If you only need the filenames you can pipe it further to grep :

head -n1 -v mydir/*|grep -B1 pattern|grep ==> 

As noticed by don_crissti in comments, beware of filenames matching the pattern themselves, though…

Источник

Оцените статью
Adblock
detector