How can I shorten this command? The goal is to display the latest 3 files that are in the ABC folder and match uvw in the file name, then do the same but match xyz in the file name.

I'm looking to shorten this since there's a need to add more strings to look for in the future.

find . -name 'ABC' | xargs ls | grep -i uvw |sort| tail -n 3; find . -name 'ABC' | xargs ls | grep -i xyz |sort| tail -n 3

Example output:

  • @don_crissti is correct. I edited my question to clarify this. – kouichi Aug 8 '18 at 18:40
  • @steeldriver, the xargs runs ls on all the folders with the name ABC in them. – kouichi Aug 8 '18 at 18:41

With zsh:

set -o extendedglob # best in ~/.zshrc
for w (uvw xyz) printf '%s\n' **/ABC/(#i)*$w*(D[-3,-1]:t)
  • **/: any level of subdirectories
  • (#i): case insensitive matching for what follows
  • (D[-3,-1]:t): glob qualifier
  • D: include hidden files and look inside hidden dirs like find does
  • [-3,-1]: select only the last 3 (globs are sorted in lexical order by default)
  • :t: modifier that extracts the tail of the file path (basename) like your ls does.

Note that if there are several ABC directories, the name of those directories will influence the sorting (files in a/ABC will appear before those in b/ABC).

  • Is this a bash command? I'm getting this error: -bash: set: extendedglob: invalid option name – kouichi Aug 8 '18 at 20:49
  • @kouichi, no, it's zsh syntax, not bash, it says at the start of the answer. – Stéphane Chazelas Aug 8 '18 at 21:37
  • Thanks, I have no idea how zsh works but I made a bash script which fits what I need – kouichi Aug 8 '18 at 23:07

This will search for any files which match any of those fragments:

for fragment in ABC UVW xyz; do 
    find . -name "\*${fragment}\*" | sort | tail -n3; 

If you want to find files which match all of them, you can do something like this:

find . -name '*ABC*' -a -name '*xyz*' | sort | tail -n3

-a is find's boolean AND operator.

For BSD find, use -or and -and rather than -o and -a.

  • 2
    -a is the implicit default between the find tests. – Kusalananda Aug 8 '18 at 18:36
  • 1
    Yes, but I prefer to be explicit when possible to prevent possible misreading by future-me. – DopeGhoti Aug 8 '18 at 18:37
  • Not sure who upvoted here but anyway, please read the question again and revise your post. – don_crissti Aug 8 '18 at 19:15

Most probably you are looking for

find . -iregex '.*/ABC/[^/]*uvw.*' | sort | tail -n3

Here's how it works:

  • -iregex search in case insensitive mode
  • .*/ABC/ look in any depth for directory ABC (the whole name of dir)
  • [^/]*uvw.* the name of dir ABC is directly followed by a filename containing uvw string, not any sub-directories in between

The sort and tail parts are untouched.

As for adding other patterns: you need to make a loop over this command with all needed strings, or if you need only 3 files regardless from which group they are coming you can combine everything in one command:

find . -iregex '.*/ABC/[^/]*\(uvw\|xyz\)[^/]*' | sort | tail -n3
  • Thanks @jimmij. 'It looks like find . -iregex '.*/ABC/[^/]*uvw.*' | sort | tail -n3' is similar to 'find . -name 'ABC' | xargs ls | grep -i uvw |sort| tail -n 3', but takes longer to search. Maybe I can loop over ABC, DEF, etc. like what you've mentioned. I'll give it a try – kouichi Aug 8 '18 at 20:55

I got what I needed with for loops in a bash script, is it possible to do this in the command line?

for i in ABC DEF
  for j in uvw xyz
    x=`find . -iname $i | xargs ls | grep -i $j | sort | tail -n 3`
    echo "$x"

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.