Say there are files with names:
batman.c
debate.c
cricketbat.c
What command and how should I use it to list all these files by using the searchtag bat
?
command linefilesfindsearch
Say there are files with names:
batman.c
debate.c
cricketbat.c
What command and how should I use it to list all these files by using the searchtag bat
?
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:
*
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:
\
can make it hard to see exactly what is being passed by the shell to the find
command."
sometimes involves somewhat complicated rules.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.
-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
Unfortunately the find
command's -name
predicate only accepts a single pattern. If you want to search for multiple files by name you'd need to chain them with the -o
(logical OR
) operator - something like:
find Documents/ \( -name "file2.txt" -o -name "file5.txt" -o -name "file9.txt" \) -print
This makes it tricky to construct the search programmatically from a list; the closest I can come to your attempted command is:
read the list into a shell array
mapfile -t files < list.txt
use the bash shell's printf
to construct the predicate list
printf -- '-name "%s" -o ' "${files[@]}"
use eval
to evaluate the resulting command string
There's a wrinkle here inasmuch as if we use printf
's format re-use feature to construct the list in this way, we're left with a 'dangling' -o
; we can work around this by terminating the list with a -false
test (since -o -false
is a Boolean no-op) so that our final predicate string becomes
"\( $(printf -- '-name "%s" -o ' "${files[@]}") -false \)"
Putting it all together - given
$ tree dir
dir
├── Folder_1
│ ├── file1.txt
│ ├── file2.txt
│ ├── file3.txt
│ └── file4.txt
├── Folder_2
│ ├── file5.txt
│ ├── file6.txt
│ └── file7.txt
└── Folder_3
├── file8.txt
└── file9.txt
3 directories, 9 files
and
$ cat list.txt
file2.txt
file5.txt
file9.txt
then
$ mapfile -t files < list.txt
$ eval find dir/ "\( $(printf -- '-name "%s" -o ' "${files[@]}") -false \)" -print
dir/Folder_1/file2.txt
dir/Folder_2/file5.txt
dir/Folder_3/file9.txt
To copy files instead of just listing them, you could then do
$ mkdir newdir
$ eval find dir/ "\( $(printf -- '-name "%s" -o ' "${files[@]}") -false \)" -exec cp -t newdir/ -- {} +
resulting in
$ tree newdir
newdir
├── file2.txt
├── file5.txt
└── file9.txt
0 directories, 3 files
Note: the eval
command is powerful and potentially open to abuse: use with care.
In practice, given that you appear to want to find only a small number of files, the KISS approach would be to accept the performance hit of multiple find
calls and just use a loop:
while read -r f; do
find dir/ -name "$f" -exec cp -v -- {} newdir/ \;
done < list.txt
or even using xargs
xargs -a list.txt -n1 -I@ find dir/ -name @ -exec cp -v -- {} newdir/ \;
Best Answer
Finding Files with
bat
AnywhereTo find all files anywhere inside
/path/to/folder
whose names containbat
, you can use:I have quoted the search pattern
*bat*
because, if the quotes were omitted and files match*bat*
in the current directory, the shell will expand*bat*
into a list of them and pass that tofind
. Thenfind
wouldn't work right. (\*bat\*
and"*bat*"
also work.)To search in the folder you're currently in (e.g., that you've
cd
ed to), use.
, as usual:To search your whole computer, use
/
. To search your home directory, use~
, or the full name of your home directory. (The shell expands~
to your home directory's fully qualified path.)Broadening or Narrowing Your Search, Based on Name
If you want to search case-insensitively, so files containing
BAT
,bAt
, and so forth are matched, use the-iname
test instead of the-name
test:I've noticed all your files end in
.c
. If you only want to find files like that, use:I noticed all your filenames have
bat
either at the very beginning or the very end of the part preceding the.c
suffix. If you want to avoid matching files likeembattled.c
, you could use:-o
is the or operator.Matching Only Files
To find only regular files--and not folders, symbolic links, and special device nodes--you can use
-type f
. This is frequently recommended and sometimes quite appropriate... but often not what you really want, especially if you're runningfind
for the purpose of examining the output yourself. If you had a symbolic link that matched your search, wouldn't you want to know about it?If you want to find both regular files and symbolic links, you can use:
That uses the
-o
operator and also parentheses for grouping (which must be quoted so the shell does not treat them specially; otherwise you'll get a syntax error).But suppose you only want to see symbolic links that ultimately point to a regular file (and not symbolic links to directories, device nodes, etc.). That's actually even simpler: use
-xtype
instead of-type
. Provided you're not runningfind
with-L
flag,-xtype
on a symbolic link tests the type of the file the link points to.If you have a symlink to another symlink to a file,
-xtype f
will match it even though its direct target is another symlink rather than a regular file. This is almost always what you want.Often people think they want
-type f
, but really they want-xtype f
.Getting Detailed Output
find
's default action if you don't specify one is-print
. All the commands given above are equivalent to themselves with-print
tacked on at the end.find
is often used to run commands based on the files found--often, commands that make changes. But there are also other actions whose purpose is to display results, besides-print
. Of particular interest is-ls
:This gives detailed information on each file, in a multi-column format, similar to (though not quite the same as) what you would see by running
ls file
.Further Reading
For more information on
find
and other ways to find files, see:find
manual page, accessible online or by runningman find
in a terminal.find
,locate
, andxargs
utilities.