3

I have multiple sub-directories each with different depths. I need to search for the last occurrence of a string in a specific file type (say *.out). How can I accomplish this?

I have tried:

grep -r 'string' **/*.out | tail -1

But that gives me only the last string of the last file.

3
  • What's the different between the last string of the last file and the last occurrence of a string?
    – cuonglm
    May 7, 2015 at 17:44
  • If I have 100 directories with each possessing a file of type .out, I need the command to print out the last occurence of string in all 100 files. The above command only prints it out for one file (which I think is the last file accessed) May 7, 2015 at 17:47
  • @cuonglm: The difference is the former omits the relevant occurrences in other files.
    – lcd047
    May 7, 2015 at 17:47

4 Answers 4

2

Assume you are in parent directory of sub directories:

find . -type f -name '*.out' -exec sh -c '
  for f do
    grep "string" /dev/null "$f" | tail -n 1
  done
' sh {} +
6
  • This works. Is it possible to obtain the filepath before the grep step? Also what does sh -c & sh {} + do? May 7, 2015 at 17:56
  • Unfortunately this will print the name of every file with this extension (independent if contain or not the string) May 7, 2015 at 17:59
  • @WanderingMind: Add /dev/null after "$f" to trick grep to show you the filename. sc -c ... is needed to make the pipe grep | tail work. sh {} + sends the filenames to sh.
    – lcd047
    May 7, 2015 at 18:01
  • @WanderingMind: I invite you to read unix.stackexchange.com/a/152723/38906 .
    – cuonglm
    May 7, 2015 at 18:01
  • 1
    @RomeoNinov: Fixed it!
    – cuonglm
    May 7, 2015 at 18:02
1

I will use something like:

for i in `find . -name "*.out" -type f`;
do
grep -l 'string' $i
grep 'string' $i|tail -1
done

With 1st grep you will have filename and below (second grep) the content.

This works as long as the file names don't contain whitespace or \[*?. See cuonglm's answer for a robust solution.

4
  • Great this works. Is it possible to get the path for the file as well before the grep May 7, 2015 at 17:52
  • From what I know about grep I can print content of only filename. To print both I can use second grep. See my addon to the answer May 7, 2015 at 17:56
  • 1
    Thank you. It works in my case. Although I think as discussed in other answer, this command will provide me paths of files that don't contain the string. May 7, 2015 at 18:04
  • No, the script above will print only files, which contain 'string' string inside May 7, 2015 at 18:09
1

With zsh on a GNU system:

for f (**/*.out(.)) tac < $f | grep --label=$f -m1 string
1

With gnu tools, you could use a single invocation of gawk:

awk '/pattern/{l=$0} ENDFILE{if (l) {print FILENAME ":" l; l=""}}' **/*.out

(if line matches store it into l; at the end of each file, if l is not empty print file name and l then reset l)

or sed:

sed -ns '/pattern/h;${x;//{F;p}}' **/*.out

(if line matches, copy to hold space; if la$t line, exchange hold space w. pattern space; if it matches, print File name and line)
though the output is different - file name and corresponding last matching line are on separate lines - but if piped to another sed e.g.

sed -ns '/pattern/h;${x;//{F;p}}' **/*.out |sed '$!N;s/\n/:/'

it will mimic grep output1.
The above assume bash (v. 4 with shopt -s globstar) or zsh. To include hidden files add shopt -s dotglob (bash) or setopt dotglob (zsh).


1: as long none of those file names contains newline characters.

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.