11

I am writing a script, I want to check if the first line of the file matches a certain pattern and if it does then print out the file. How can I achieve this?

How do I check for pattern? Is there a way to check for pattern and based on the output do something..

EDIT: Please take a look at this question: https://stackoverflow.com/questions/5536018/how-to-get-match-regex-pattern-using-awk-from-file

I want something like this, but none of them worked for me. I basically want to check if the first line matches a regex pattern or not and based on that print the lines of file.

  • 1
    What is the output you're expecting? What is the pattern you're looking for? What have you tried so far? – tachomi Sep 28 '15 at 19:57
  • @tachomi edited please take a look – Mathew Sep 28 '15 at 20:08
17

You could do that with ed:

ed -s infile <<\IN 2>/dev/null
1s/PATTERN/&/
,p
q
IN

the trick here is to try to replace PATTERN on 1st line with itself. ed will error out if it can't find the specified pattern so ,p (print whole file) will only be executed if 1s/PATTERN/&/ is successful.

Or with sed:

sed -n '1{
/PATTERN/!q
}
p' infile

this quits if the first line does not (!) match PATTERN, otherwise it prints all lines.
Or, as pointed out by Toby Speight, with GNU sed:

sed '1{/PATTERN/!Q}' infile

Q is the same as q but it does not print the pattern space.

  • You could Q instead of q for GNU sed, or d before q (portable) so as not to require the -n flag and p command: sed '1{/PATTERN/!Q}' infile or sed -e '1{' -e '/PATTERN/!{' -e 'd' -e 'q' -e '}' -e '}' infile, respectively. – Toby Speight Sep 29 '15 at 16:27
  • d restarts the command cycle That always catches me out! :-| – Toby Speight Sep 30 '15 at 11:49
  • With GNU sed the first sed command complains sed: -e expression #1, char 10: extra characters after command (because of the p), but the ed and last sed suggestions work fine. – Skippy le Grand Gourou Aug 25 '17 at 8:37
  • NB : Solutions provided by this answer have the merit, over other answers, that they can be applied on a pipe. – Skippy le Grand Gourou Aug 25 '17 at 8:55
  • 1
    @SkippyleGrandGourou - you tried turning it into a one-liner without separating the commands with semicolons - this is the proper way to do it sed -n '1{/PATTERN/!q};p' – don_crissti Aug 25 '17 at 8:56
15

With POSIX tools chest:

{ head -n 1 | grep pattern && cat; } <file
  • 1
    { double } <sweet. – mikeserv Sep 29 '15 at 3:20
  • @mikeserv: I intend to use it to prevent new person from confusing, but Stephane edited is clearer. – cuonglm Sep 29 '15 at 10:18
8
 awk '/pattern/{print FILENAME}; {nextfile}' ./*.txt

would print the name of the non-hidden txt files in the current directory whose first line matches the extended regular expression pattern with those awk inplementations that support nextfile.

If instead of printing the file name, you want to print the whole file content, you can do:

 awk 'FNR == 1 && ! /pattern/ {nextfile}; {print}' ./*.txt

That's efficient in that it runs only one command, but awk being not the most efficient command to dump the content of a file, with large files, you could possibly obtain better performances by doing something like:

 awk '/pattern/{printf "%s\0", FILENAME}; {nextfile}' ./*.txt |
   xargs -r0 cat

That is, only use awk to print the list of files that match (0-delimited) and rely on cat to dump their content.

6

If you're writing a shell script, you could so something like

for file in ./*; do head -n 1 "$file" | grep -q 'PATTERN' && cat "$file"; done

Or, in Perl:

perl -Tlne '$f = /PATTERN/ if $. == 1; print if $f; $. = 0 if eof' ./*
  • @Stéphane Chazelas: Maybe close ARGV is more idiom than assigning to $.. – cuonglm Sep 29 '15 at 10:58
  • @terdon Yours looks like code golf, all in one line, no brackets around the variable names and is not encouraging clean structure. And you had a missing dollar sign when I posted, that's just not the way to teach bash. I assume those factors come from the perl background you also seem to have, so you shall be forgiven! ;) – user136324 Sep 29 '15 at 21:01
  • @guest hi and welcome to the site! I converted your answer to a comment since answers should only be posted if they are answering the actual question. This is not a forum in the classic sense and we only want pure Q&A here. You might want to take a look at the help center or take the tour to understand the site better. That said, my background is actually in biology so yes, my code is far from clean :) However, I don't see how brackets would help here, the quotes already protect the variable. What would break this that brackets would protect from? – terdon Sep 29 '15 at 21:33
  • @guest ah, sorry, forgot you can't comment. Feel free to come and explain in chat, I'm sure I might learn something. – terdon Sep 29 '15 at 21:34
5

Oldschool, just translate your sentence into standard commands:

for file in *; do
    if head -n 1 "${file}" | grep -q 'PATTERN'; then
        cat "${file}"
    fi
done

For learning bash that is a good start. If you just need a quick solution, try the sed-, awk- or perl-answers. Both nice, but they are own languages you need (and probably want) to learn.

It's a pretty simple example, so if you want to learn more, you could also try the same in ruby, php, js (e.g. in nodejs) or any other language that allows file access. Even C/C++ or Java should be easy to manage with a small task.

  • 1
    This is basically the same as mine except that you use if/else instead of [ ] &&. – terdon Sep 29 '15 at 11:09

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.