11

I want to list all the log files which do not belong to today and only the files that contain 2015 in their names.

These are the commands I have tried:

find . -name 2015 -mtime +1 -exec  ls -ltrh  {} \;
find . -name *2015* -mtime +1 -exec  ls -ltrh  {} \;

The output from both is nothing, but still, files are there. Something is wrong with these commands, and I am not getting what. Any help would be appreciated.

3
  • The find part looks good to me, but I also had problems with the -exec parameter. I couldn't get that to work either. (I was trying to move all jpg files from one folder to its parent directory through it - and ended up doing it manually...)
    – Byte Commander
    Apr 8, 2015 at 1:24
  • 2
    You may want to take a look at Why does find -mtime +1 only return files older than 2 days? Apr 8, 2015 at 2:40
  • @steeldriver I think that's really what's going wrong here, since if there were files in the current directory with 2015 in their name (the other obvious shortcoming of the way the command is written) I suspect there would be some kind of output. So you might want to post an answer about that. Apr 8, 2015 at 2:44

3 Answers 3

20

The command find . -name '*2015*' -mmin +1440 -ls will probably do what you want. See below for details.

Your first command had -name 2015. It did not work because it finds only files whose names are exactly 2015, with no other characters in them.

Your second command, find . -name *2015* -mtime +1 -exec ls -ltrh {} \;, might have failed for a couple of reasons:

1. Unquoted * characters are expanded by the shell, then passed on to find.

If there are any files directly contained in the current directory (the one you're in when you ran that find ... command) whose names contain 2015 (and don't start with a .), then the shell expanded *2015* into a list of those filenames, then passed that list as arguments to find. This is not what you want--instead, you want to pass *2015* literally as an argument to find, so that find, and not the shell, can find which files match it.

To fix that problem, quote *2015*. There are three common ways to do it:

  • '*2015*' (i.e., find . -name '*2015*' -mtime +1 -exec ls -ltrh {} \;)
  • "*2015*" (i.e., find . -name "*2015*" -mtime +1 -exec ls -ltrh {} \;)
  • \*2015\* (i.e., find . -name \*2015\* -mtime +1 -exec ls -ltrh {} \;)

I suggest writing it with single quotes as '*2015*', because:

But in this case it doesn't really matter. ' and " both treat * the same and the expression isn't complicated enough for \ quoting to make it hard to understand.

2. -mtime +1 only selects files modified two or more days ago.

As man find says:

       Numeric arguments can be specified as

       +n     for greater than n,

       -n     for less than n,

       n      for exactly n.
       -mtime n
              File's data was last modified n*24 hours ago.  See the  comments
              for -atime to understand how rounding affects the interpretation
              of file modification times.
       -atime n
              File was last accessed n*24 hours ago.  When  find  figures  out
              how  many  24-hour  periods  ago the file was last accessed, any
              fractional part is ignored, so to match -atime +1, a file has to
              have been accessed at least two days ago.

Suppose a file was modified 47 hours ago. To figure out how many 24-hour periods that is, find rounds down: it is one 24-hour period ago. But -mtime +1 matches only files whose modification times are strictly more than one 24-hour period ago. Thus files from yesterday are not matched.

See Why does find -mtime +1 only return files older than 2 days? for more information, as suggested by steeldriver.

To find files last modified anytime more than 24 hours ago, I suggest instead stipulating it as 1440 minutes ago with -mmin +1440:

find . -name '*2015*' -mmin +1440 -exec ls -ltrh {} \;

Some readers might be wondering why I did not quote {}. Some people quote {} to remind humans that it is not an expression for brace expansion. Bourne-style shells (like bash) don't require {} with nothing inside to be quoted. Maybe some non-Bourne-style shell does treat it specially; that might be why some users quote it. But there's also a misconception that one must sometimes quote {} so -exec handles filenames with spaces correctly. That;s false: with {} or '{}', find gets the same arguments, as the shell removes the quotes before passing '{}' to find. To counter this misconception, I don't quote {}, but it's a matter of style--if you prefer {} to document how the shell isn't treating { and } specially, that's fine.

I recommend you also either change your ls command, or (as muru has suggested) replace it with find's -ls action. ls -ltrh is probably not doing what you intend because it is run separately for each file found and thus the -t and -r flags, which specify sorting, are irrelevant.

Though the output will be formatted a bit differently than with ls -l, using -ls is simpler and easier.

find . -name '*2015*' -mmin +1440 -ls

Or if you decide you really only need to list the file's names (including their paths, relative to .), you can simply specify no action, causing the default -print action to be used:

find . -name '*2015*' -mmin +1440
3
  • 1
    Nice thorough answer! at least with GNU find, there's also the newerXY syntax, which accepts calendar arguments e.g. -newermt 'yesterday' and ! -newermt 'yesterday' Apr 8, 2015 at 11:02
  • You know what ? You are awesome.@Eliah Kagan
    – Raja G
    Apr 8, 2015 at 11:44
  • Thanks stell driver , thats very good information from the link to know. But in my case as it is application log from application old files got no way to get modified. Thank you again
    – Raja G
    Apr 8, 2015 at 11:47
2

Quote the expression and I suggest you quote the brackets as well

find . -name "2015" -mtime +1 -exec  ls -ltrh  '{}' \;

or

find . -name "*2015*" -mtime +1 -exec  ls -ltrh  '{}' \;

you do not need to pass the files you find to ls

find . -name "*2015*" -mtime +1
2
  • Maybe restrict using -type f? And if ls has to be used, suggest using -ls.
    – muru
    Apr 8, 2015 at 2:18
  • 1
    sure, all good suggestions. I am not sure what the OP wants and I believe the source of the problem is the lack of quotes around the "-name" search pattern.
    – Panther
    Apr 8, 2015 at 2:21
2

In zsh, you can use glob qualifiers (this is a unique zsh feature, not available in other shells such as bash).

ls -ltr *2015*(m+1)

This only lists files in the current directory; to traverse directories recursively, use

ls -ltr **/*2015*(m+1)

With find, -name 2015 only finds files whose name is exactly 2015. -name *2015* only works if there are no files whose name contains 2015 in the current directory, because the shell expands the pattern *2015* before find is invoked. You need to quote the pattern:

find -name '*2015*' -mtime +1 …

Note that both find -mtime +1 and zsh's *(m+1) find files that are at least 48 hours old, not files older than today. The plus sign means “strictly more than” and the number of days is rounded down. To find files that are at least 24 hours old, use -mtime +0 or (m+0).

If you want to find files that were last modified yesterday or before, you can use find with the -newermt predicate:

find -name '*2015*' ! -newermt 'today 0:00' …

There's no really convenient way to make this test in zsh.

0

You must log in to answer this question.

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