Find the last occurence of a string in a given filetype in all subdirectories

8,022

Solution 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.

Solution 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 {} +

Solution 3

With zsh on a GNU system:

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

Solution 4

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.

Share:
8,022

Related videos on Youtube

rambalachandran
Author by

rambalachandran

An engineer trying to get insights from data

Updated on September 18, 2022

Comments

  • rambalachandran
    rambalachandran over 1 year

    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.

    • cuonglm
      cuonglm almost 9 years
      What's the different between the last string of the last file and the last occurrence of a string?
    • rambalachandran
      rambalachandran almost 9 years
      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)
    • lcd047
      lcd047 almost 9 years
      @cuonglm: The difference is the former omits the relevant occurrences in other files.
  • rambalachandran
    rambalachandran almost 9 years
    Great this works. Is it possible to get the path for the file as well before the grep
  • Atul Vekariya
    Atul Vekariya almost 9 years
    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
  • rambalachandran
    rambalachandran almost 9 years
    This works. Is it possible to obtain the filepath before the grep step? Also what does sh -c & sh {} + do?
  • Atul Vekariya
    Atul Vekariya almost 9 years
    Unfortunately this will print the name of every file with this extension (independent if contain or not the string)
  • lcd047
    lcd047 almost 9 years
    @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.
  • cuonglm
    cuonglm almost 9 years
    @WanderingMind: I invite you to read unix.stackexchange.com/a/152723/38906 .
  • cuonglm
    cuonglm almost 9 years
    @RomeoNinov: Fixed it!
  • rambalachandran
    rambalachandran almost 9 years
    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.
  • Atul Vekariya
    Atul Vekariya almost 9 years
    No, the script above will print only files, which contain 'string' string inside