Updated: 8 October 2026 · Applies to: GNU findutils 4.10 and coreutils 9.7 on Debian 13 and Ubuntu 26.04; findutils 4.8 and coreutils 8.32 on AlmaLinux 9
Put the pattern in quotes when find should do the matching, leave it unquoted when you want the shell to expand it for ls, cp, mv or rm, and preview every destructive command with echo first:
find /var/www -name '*.php' # quoted: find matches, in every subdirectory ls -d -- *.log # unquoted: the shell expands, current directory only echo rm *.bak # dry run: prints the exact command rm would receive
How *, ? and [ ] work is explained in Linux wildcards and globbing; this article covers using them safely. The examples ran in a test folder with access.log, error.log, notes.txt, logs/app.log, logs/old/app-2025.log and a site folder.
Wildcards with find: always quote
Unquoted, the shell replaces *.log with the matching names in the current folder before find starts, and find fails:
$ find . -name *.log find: paths must precede expression: `error.log' find: possible unquoted pattern after predicate `-name'? $ find . -name '*.log' ./logs/app.log ./logs/old/app-2025.log ./error.log ./access.log
Which test to use:
-namematches the file name only, never the folder part.find . -name 'logs/*.log'finds nothing.-inameignores case:find site -iname '*.php'also listedsite/uploads/shell.PHP, a PHP file in an upload folder that deserves a closer look.-pathmatches the whole path as find prints it, starting with./. Here*also matches a slash, sofind . -path './logs/*.log'returned./logs/app.logand./logs/old/app-2025.log. To limit the depth, add-maxdepth.- Combine tests:
find . -path '*/uploads/*' -iname '*.php'printed only./site/uploads/shell.PHP.
For several patterns, group them with escaped brackets. Without the brackets, -print binds only to the last pattern:
$ find . -type f \( -name '*.log' -o -name '*.txt' \) ./notes.txt ./logs/app.log ./logs/old/app-2025.log ./error.log ./access.log $ find . -type f -name '*.log' -o -name '*.txt' -print ./notes.txt
! excludes a pattern: find . -maxdepth 1 -type f ! -name '*.log' printed ./notes.txt. More examples: search for multiple file extensions with find and looking for files.
Dry runs before you delete or move
- Preview shell wildcards with echo.
echo rm *.bakprints the command after expansion. If it printsrm *.bakunchanged, nothing matched. - Run find without an action, then add
-deleteat the end. Order matters: find reads the expression from left to right, and-deleteacts on every file that reaches it. On a copy of the test folder,find . -delete -name '*.log'deleted every file and folder, not just the logs. The safe form isfind . -name '*.log' -delete. - Ask for confirmation.
rm -Iasks once when you remove more than three files (rm: remove 5 arguments?),rm -iasks for each file, andfind ... -ok rm {} \;shows each command first:< rm ... ./logs/old/app-2025.log > ?
Surprises with cp and mv
Two matches and no target. If a wildcard expands to exactly two files and you forget the destination, cp copies the first over the second:
$ echo cp *.conf cp a.conf b.conf $ cp -v *.conf 'a.conf' -> 'b.conf' $ cat b.conf server A
The original b.conf is gone. With three matches, cp refuses: cp: target 'c.conf': Not a directory (AlmaLinux 9 words it is not a directory).
The target folder matches too. mv * archive/ moves everything else but reports mv: cannot move 'archive' to a subdirectory of itself, 'archive/archive'. Hidden files are not moved, because * skips names that start with a dot.
No match. mv *.bak archive/ gives mv: cannot stat '*.bak': No such file or directory: the pattern was passed on literally.
File names that start with a dash
A file called -r turns into an option once the shell expands *. In a folder with -r, old.txt and a subfolder keep:
$ echo rm * rm -r keep old.txt
That would delete keep and everything in it. Put -- before the wildcard (end of options) or use ./*, so every name is treated as a file:
$ rm -- * rm: cannot remove 'keep': Is a directory
-r and old.txt were removed and the folder survived.
Many files or files in subfolders: let find run the command
Shell wildcards only look in one folder, and the expanded list must fit in about 2 MB (getconf ARG_MAX printed 2097152). In a folder with 50,000 cache files with names of about 40 characters, rm *.log failed with /usr/bin/rm: Argument list too long; 50,000 short names such as f1.log still fitted. Let find pass the files in batches with {} +:
$ find . -name '*.log' -exec mv -v -t archive/ {} +
renamed './logs/old/app-2025.log' -> 'archive/app-2025.log'
Add -mtime +30 to pick files changed more than 30 days ago. For other commands, find ... -print0 | xargs -0 command also handles names with spaces. For deleting, see how to delete files in Linux.
ls as a preview: ls * also lists the contents of every matching folder. ls -d -- * shows only the names the wildcard matched.
On Ubuntu 26.04, ls is the Rust uutils version, but cp, mv and rm are still GNU coreutils 9.7 (links to gnucp, gnumv and gnurm) and find is GNU findutils 4.10.0. Every example gave the same output there as on Debian 13. Ready-made one-liners are in 10 practical wildcard examples.
Ucartz services for this topic
- KVM VPS hosting: NVMe SSD and full root access, with Free Basic Managed Support.
- Unmanaged dedicated servers: full root access and free IPMI KVM, with Free Basic Managed Support.
- Linux server management: hardening, patching, monitoring and automation for Ubuntu, Debian, RHEL and AlmaLinux.
