5

How can I use grep to find a string in files, but only search in the first line of these files?

5
  • 5
    grep -n . file.glob | grep "^1:.*search string"
    – rob
    Mar 23, 2015 at 13:32
  • 1
    grep root <(head -1 /etc/passwd)
    – c4f4t0r
    Mar 23, 2015 at 14:07
  • 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.
    – Sig-IO
    Mar 23, 2015 at 14:39
  • 1
    @peterh Rather than asking the question again, it is better to flag the current question for migration there.
    – kasperd
    Mar 23, 2015 at 15:25
  • 1
    Is there a reason you have to use grep? Rather than, say sed or awk? Apr 7, 2017 at 8:32

10 Answers 10

10

Two more alternatives :

With awk

awk '{if ($0~"pattern") print $0; nextfile;}' mydir/*

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

awk 'FNR==1{if ($0~"pattern") print $0;}' mydir/*

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<10 for the 10 first lines, etc. (I guess in this case, if you are dealing with very large files and your awk version supports it you may want to mix FNR with nextfile to improve performances.)

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…

0
2

I have implemented the comment of @Rob and succeeded to get the desired result.

Replace string by your string.

grep -Rin "string" . | grep ":1:.*string" > result.txt

This does a recursive case-insensitive search for string in the current directory and prints the line numbers. Then it searches for occurrences in files which are on line 1 and saves the output to a file called result.txt.

1
  • 2
    This would also find any line in which :1: happens to occur before string, no matter what line that is. E.g., a line saying This is it:1:st string!.
    – Kusalananda
    Apr 7, 2021 at 20:52
2

Using grep only:

grep -m1 ^ * |grep 'pattern'

recursively:

grep -rm1 ^ . |grep 'pattern'
2
  • Yes. -m set to STOP reading after Num matching lines so -m1 means after one line
    – Gal Bracha
    Aug 24, 2021 at 16:52
  • @GalBracha please see me edit now, using . wouldn't really match on first line, but first non-empty lines, but with ^ it does match on first line combined with the -m1 option. Aug 25, 2021 at 14:48
1

Here is a perl onliner to do just that

perl -ne 'print if /MY_SEARCH_STRING/; exit' myfile.txt

This is going to check if the keyword MY_SEARCH_STRING is present in the first line of the file myfile.txt. If you need to search in the entire file just remove exit from the oneliner.

1
  • Note that one needs to loop on the list of files if there are several. That is, globbing (using wildcard, e.g. mydirectory/*) won't work. Apr 6, 2017 at 13:44
1

we can try with sed command too

sed -n '1p' filename| sed -n '/pattern/p'
1
  • 2
    You can even combine the two tests: sed -n '1{/pattern/p}'. To make it more efficient, quit immediately: sed -n '1{/pattern/p};q'. Aug 25, 2021 at 7:13
0

Here is a (bad) example of a perl-script that would do something like this:

#!/usr/bin/perl -w

foreach (@ARGV) {
    my $filename = $_;
    open my $file, '<', $filename; 
    my $line = <$file>; 
    close $file;

    print "$filename\n" if $line =~ /your-match-text/;
}
0

Do you want the list of full strings as a result, or do you want a list of files that contain the string? This would search line number one(-n1) of shell scripts(*.sh) and collect the strings containing 'bash':

head -n1 *.sh | grep bash > fullstring.txt

fullstring.txt would contain something like this:

#!/bin/bash
#!/bin/bash
1
  • Beware, better use the -q option of head to prevent accidental matching against the filename. Apr 7, 2017 at 8:21
0

Using find to call head and grep:

find . -type f -exec head -n 1 {} \; | grep -e "$pattern"

This outputs the first line of each regular file in the current directory or below. This is passed to grep which extracts the lines that matches the pattern stored in the variable pattern.

Using sed instead:

find . -type f -exec sed -n '/pattern/p;q' {} \;

This invokes sed for each regular file. The sed expression prints the current line if it matches the pattern pattern. It then immediately quits.

-1

Try something like this. Create file finder.sh with this content. Change parameters in the file to fit your needs.

#!/bin/bash

# Where to search
DIR="/path/search/dir"

# Search string
SEARCH="my-string"

FILES=$(find "$DIR" -type f)

for F in $FILES; do
   head -1 $F | grep -w "$SEARCH"
done

Save the file and chmod +x finder.sh

Execute ./finder.sh

Note: if you going to search in files with root privileges you need to use sudo or root user.

-1

Try:

$ case "$(head -n 1 < file)" in (*pattern*) echo Match ;; esac
Match

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.