18

I have a directory that I am trying to clean out that contains both files and subdirectories. What I am trying to do is simple: move all the files into another directory, but leave all the sub-directories as they are.

I am thinking something like:

mv [*_but_no_dirs] ./other_directory

It seems like there should be a simple way to do this with wildcards * and regex...

Anyone have ideas?

  • 1
    So move all the files in a subfolder, but ignore any in the subdirectories? – Wilf Jul 29 '14 at 18:37
  • @Wilf: Exactly. – Questionmark Jul 29 '14 at 18:38
  • 2
    In zsh, you could do mv **/*(.) ./other_directory - with bash you'd need to resort to external commands like find, though. – godlygeek Jul 29 '14 at 18:47
15

Regex aren't involved here. Wildcards in bash (like most other shells) only match files based on the file names, not based on the file type or other characteristics. There is one way to match by type: adding / at the end of the pattern makes it only match directories or symbolic links to directories. This way, you can move directories, then move what's left, and move directories back — cumbersome but it works.

tmp=$(TMPDIR=.. mktemp -d)
mv -- */ "$tmp"
mv -- * other_directory/
mv "$tmp"/* .
rmdir "$tmp"

(that approach should be avoided if the current directory is the mount point of a filesystem, as that would mean the moving of directories away and back would have to copy all the data in there twice).

A standard way to match files by type is to call find.

find . -name . -o -type d -prune -o -exec sh -c 'mv "$@" "$0"' other_directory/ {} +

(also moves symlinks, whether they point to directories or not).

In zsh, you can use glob qualifiers to match files by type. The . qualifier matches regular files; use ^/ to match all non-directories, or -^/ to also exclude symbolic links to directories.

mv -- *(.) other_directory/

In any shell, you can simply loop.

for x in *; do
   if ! [ -d "$x" ]; then
     mv -- "$x" other_directory/
   fi
done

(does not move symlinks to directories).

| improve this answer | |
  • The first approach could also fail if some of the directories are mount points or are not owned by you (even if you have write access to the current working directory). Also note that mktemp is not a standard command. – Stéphane Chazelas Aug 17 at 8:19
12

You could use something like

find . -maxdepth 1 \( ! -type d \) -exec sh -c 'mv  "$@" MYDIR' _ {} \;

First we use find to look only within the current directoy, then we ignore directories by using ! -type d finally we execute sh and move everything to the destination dir. You might try {} + at the end which will be faster.

| improve this answer | |
  • 1
    Why do you suggest -exec sh -c 'mv "$@" MYDIR' _ {} \; rather than -exec mv {} MYDIR \;? Just to get something that can be streamlined by changing the \; to a +? You can do that with the -exec mv -t MYDIR {} + form. – Scott Jul 29 '14 at 19:20
  • @Scott I had no idea about the -t flag. I also explained in the answer that the + is faster but not all find versions support it. So the above code is pretty much compatible with any find or shell that is used. – Valentin Bajrami Jul 29 '14 at 19:58
  • 3
    @val0x00ff: “…the above code is pretty much compatible with any find or shell that is used.” True, but so is find … -exec mv {} MYDIR \;, and that uses less resources than … -exec sh -c 'mv "$@" MYDIR' _ {} \;. – Scott Jul 29 '14 at 20:14
  • @Scott is right; this is an odd command. + is specified in POSIX anyway. – Wildcard Dec 3 '16 at 1:17
2

It's potentially a bit dangerous but

cp * destination/
rm *

As both cp and rm won't operate on directories without the -r switch.

| improve this answer | |
  • 2
    Clever, but surely living on the edge – Balu Jun 19 at 14:14
  • That has several issues: (1) it copies the files data, so if destination is on the same file system, it's going to be a lot less efficient than mv that just does a rename. (2) if fails if there are filenames starting with - (or have bad consequences, think of a -t.. file for instance. (3) for symlinks, it copies the target (provided it's a directory) instead of the symlink. (4) it doesn't preserve metadata (5) it breaks hard links... – Stéphane Chazelas Aug 17 at 8:00
1

Here is a simple 1 line command to achieve what you are trying to do:

find ./sourceDir -maxdepth 1 -type f | xargs mv -t ./targetDir

Explanation

  1. Find all the files you want at depth 1 only, that are of type files. You can find explanation here.
    https://linux.die.net/man/1/find

  2. Pipe that using "| xargs" to mv.
    https://linux.die.net/man/1/xargs

  3. tell mv where to move the files
    https://linux.die.net/man/1/mv

This 1 liner is fairly portable, and can be run from anywhere in the system as long as you have the sourceDir and targetDir, either as full paths or relative paths.

| improve this answer | |
0

mv $(find /my/source/directory -maxdepth 1 -type f) /my/destination/directory

Move will take a list of arguments, so when you ask find to give you everything that is of type "file" within /my/source/directory, it will move them one by one to /my/destination/directory. If you wanted to include the files in the subdirectories, too, while leaving the directory structure intact, remove -maxdepth 1 from the above command.

| improve this answer | |
0
if [ -f "$PATH/$FILE_NAME" ]; then mv "$PATH/$FILE_NAME" "$OTHER_PATH/$FILE_NAME"; fi;
| improve this answer | |
-1

I would suggest simply doing mv *.* destination/ with destination/ being the folder you are moving to.

| improve this answer | |
  • 2
    check out: unix.stackexchange.com/help/formatting – Jeff Schaller Dec 3 '16 at 1:41
  • 2
    Directory names can contain 'dot' and filenames can exclude it (and often do) so *.* will move some directories and leave some files, which is not what the asker wanted. – dave_thompson_085 Dec 3 '16 at 5:18
  • Sorry but this is completely wrong. It will simply move anything with a dot in its name and not move anything without one. Dots are in no way exclusive to files and they're not required by files either, so this will just move a random selection of files and directories. – terdon Apr 14 '17 at 16:25
-1

If you don't intend to move dotfiles (e.g. ".foobar"), too, you can simply do:

mv * /some/destination/

If you do, see the above answer with find's -maxdepth.

| improve this answer | |
  • 2
    The question says “move all the files …, but leave all the sub-directories as they are.”  This will move everything.   When you’re adding a new answer to a six-year-old question that already has seven answers, you might want to glance at them to see whether you have totally overlooked a key condition of the question. – G-Man Says 'Reinstate Monica' Aug 17 at 4:42
-2
mv $(ls -l | grep "^-" | awk '{print $9}') ./folder_where_move
| improve this answer | |

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.