I am trying to set up a file search which fulfils all of the following:

  1. Search recursively within a directory which includes wildcards (ex: /home/user/level_1/*/level_3/*/level_5/ ... should search within "level_5" and all its subdirectories)
  2. Search contents of a file (ex: "text in file")
  3. Define a filename to search (ex: script.js) (might be able to be combined with #1?)
  4. Display results with path and context of search string (ex. /home/user/level_1/level_2/level_3/level_4/level_5/script.js Here is the text in file ... exact format can be different but should contain this information. Timestamp is not required. Would be a bonus if instead of the full path, it only displayed the directory which is represented by level_2 in the example above, but it's not a requirement.)
  5. Display results sorted by file modified date, from oldest to newest.

I have this command below which fulfils 1-4.

grep -r --include=\script.js "text in file" /home/user/level_1/*/level_3/*/level_5/

Any ideas to include the 5th? I've seen various explanations to accomplish similar things with find or using zsh but I've not been able to put it all together.

1 Answer 1


Since you're using zsh, it should just be a matter of:

grep -H 'text in file' /home/user/level_1/*/level_3/*/level_5/**/script.js(.Om)

Where **/ matches any level of subdirectories, the . qualifier restricts to regular file (like GNU grep does with -r), and Om Orders in reverse by modification time (like ls -rt). Add the D qualifier if you also want to look for hidden ones (like GNU grep's -r does).

-H (a GNU extension, like -r or --include) makes sure the file name is printed even if only one file is found by zsh.

To print the level 2 directory instead of the file path, still with GNU grep, you can do:

for f (/home/user/level_1/*/level_3/*/level_5/**/script.js(N.Om))
  grep -H --label=${f:h5:t} 'text in file' < $f

Where :h5 takes 5 head components (the /home/user/level_1/level_2 in /home/user/level_1/level_2/and/more...), and :t the tail of that (level_2).

To also include the timestamp, you can use zsh's stat builtin to retrieve it:

zmodload zsh/stat
for f (/home/user/level_1/*/level_3/*/level_5/**/script.js(N.Om)) {
  stat -LF%FT%T.%3.%z -A t +mtime $f
  grep -H --label="${f:h5:t} ($t)" 'text in file' < $f

Here with timestamps formatted in their standard unambiguous format with ms precision like 2022-12-01T09:30:00.895+0000, but you can adapt the format to your liking with -F (uses strftime() formatting).

  • Brilliant! Adding (.Om) worked! Many thanks. I don't seem to be able to get --label to work. Copying the second command exactly, but replacing the label with --label=test still displays the filename. Any thoughts of what might be missing?
    – Alvin
    Dec 5, 2022 at 17:58
  • Figured it out. Seems we needed < before $f. for f (/home/user/level_1/*/level_3/*/level_5/**/script.js(.Om)) grep -H --label=${f:h5:t} 'text in file' < $f. Now label does show the level_5. I'm thrilled with this!!
    – Alvin
    Dec 5, 2022 at 18:16
  • @Alvin. Indeed. My bad. Fixed now thanks Dec 5, 2022 at 18:22

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .