How can I use grep to find a string in files, but only search in the first line of these files?
-
5grep -n . file.glob | grep "^1:.*search string"– robMar 23, 2015 at 13:32
-
1grep root <(head -1 /etc/passwd)– c4f4t0rMar 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-IOMar 23, 2015 at 14:39
-
1@peterh Rather than asking the question again, it is better to flag the current question for migration there.– kasperdMar 23, 2015 at 15:25
-
1Is there a reason you have to use grep? Rather than, say sed or awk?– Toby SpeightApr 7, 2017 at 8:32
10 Answers
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…
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
.
-
2This would also find any line in which
:1:
happens to occur beforestring
, no matter what line that is. E.g., a line sayingThis is it:1:st string!
.– Kusalananda ♦Apr 7, 2021 at 20:52
Using grep
only:
grep -m1 ^ * |grep 'pattern'
recursively:
grep -rm1 ^ . |grep 'pattern'
-
Yes.
-m
set to STOP reading after Num matching lines so -m1 means after one line 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.– αғsнιηAug 25, 2021 at 14:48
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.
-
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
we can try with sed command too
sed -n '1p' filename| sed -n '/pattern/p'
-
2You 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
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/;
}
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
-
Beware, better use the
-q
option ofhead
to prevent accidental matching against the filename. Apr 7, 2017 at 8:21
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.
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.