I'm trying to find all the files that are inside directories whose names start with 80s.

ls 80s*/* finds the correct files, but find . -ipath '80s*/*' returns nothing. What is the correct find command for this task?

I need to use find since I want to move all such files to a different folder and don't want to parse the output of ls.

  • 1
    Note that in ls 80s*/*, it's not ls that finds the files, it's the shell that expands 80s*/* into a list of file names that it then passes to ls (and for those of type directory, ls lists their contents, while for files of other type it just prints them). Jan 15 at 6:21

-ipath matches the complete path name, including the current path .. So you will have to include that in your pattern:

find . -ipath './80s*/*'

The above will also output directories. To list only regular files, use:

find . -ipath './80s*' -type f

(or ! -type d for any type of file except directories)

  • 6
    Also note that -ipath (a non-standard extension) matches file paths case insensitively so would also match on ./80S while shell globs do not case insensitive matching by default. Also -ipath './80s*/*' would match on ./80s/A/B/C while the shell glob equivalent would not generate that file (you'd need -maxdepth 2 in find). find would also include hidden files while shell globs don't by default. GNU find on GNU systems at least would also exclude files whose name doesn't form valid text in the locale. Jan 15 at 6:27
  • 1
    I was feeling comfortable reading your comment, @StéphaneChazelas, but the last sentence was alarm sensors going off – I have scripts that backup specific stuff using find and I have never bothered to check whether files have locale-valid text panics! Jan 15 at 10:33
  • @MarcusMüller, see the section about Bytes vs characters at Why is looping over find's output bad practice? Jan 15 at 11:00

If, as you say, 80s*/* matches the correct files, then I don't really see a reason to use find at all.

To move all names matching 80s*/* to some destination directory, destdir, you may use

mv -n 80s*/* destdir/

This moves all non-hidden names from any directory matching 80s* into the single directory destdir. The non-standard -n option usually means "no clobber", i.e. don't overwrite files if there are name collisions.

If using GNU mv, you may additionally avoid overwriting files that has the same names as other files using the --backup option (e.g., --backup=numbered), or, as above, -n (--no-clobber).

If you need to pick out only regular files, or if 80s*/* expands to a far too long list of names, use a loop:

for name in 80s*/*; do
    [ ! -f "$name" ] && continue
    mv -n "$name" destdir/

(That would also move symbolic links to regular files. To avoid these, additionally test with -h/-L whether "$name" is a symbolic link or not.)

In the zsh shell, you could move all regular files that match your pattern using

mv -n 80s*/*(.) destdir/

... where the glob qualifier (.) filters the results of the preceding pattern to only contain regular files ((-.) to also include symlinks to regular files).

If you really need to use find, then consider

find ./80s*/ -path './80s*/*' -prune ! -name '.*' -type f \
    -exec mv -n {} destdir/ \;

This matches the names that you are interested in, and avoids hidden names (you said 80s*/* matches correctly and I assume this means hidden names are not wanted). It prunes all matching names, meaning it avoids entering subdirectories that matches the pattern, and then, if the name corresponds to a regular file, moves the file to destdir using mv -n as before.

With GNU mv, you could make it slightly more efficient by using -exec mv -n -t destdir/ {} + in place of the original -exec. This would execute mv with batches of found filenames instead of calling mv once for each name.


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.