59

I am wondering - how can I move all the files in a directory except those files in a specific directory (as 'mv' does not have a '--exclude' option)?

Tunaki
  • 132,869
  • 46
  • 340
  • 423
David Liu
  • 16,374
  • 12
  • 37
  • 38

12 Answers12

107

Lets's assume the dir structure is like,

|parent
    |--child1
    |--child2
    |--grandChild1
    |--grandChild2
    |--grandChild3
    |--grandChild4
    |--grandChild5
    |--grandChild6

And we need to move files so that it would appear like,

|parent
    |--child1
    |   |--grandChild1
    |   |--grandChild2
    |   |--grandChild3
    |   |--grandChild4
    |   |--grandChild5
    |   |--grandChild6
    |--child2

In this case, you need to exclude two directories child1 and child2, and move rest of the directories in to child1 directory.

use,

mv !(child1|child2) child1

This will move all of rest of the directories into child1 directory.

Chathura Kulasinghe
  • 2,640
  • 5
  • 23
  • 23
  • 26
    Tip: Note however that using this pattern relies on `extglob`. You can enable it using `shopt -s extglob` (If you want `extended globs` to be turned on by default you can add `shopt -s extglob` to .bashrc) – Boris D. Teoharov Aug 20 '14 at 19:03
  • 19
    I keep getting `-bash: !: event not found` when trying to do this command. – FilBot3 Dec 04 '14 at 01:10
  • 3
    Derp, just realized the comment above is required for bash. – FilBot3 Dec 04 '14 at 01:14
  • 1
    How can this be forced? when there are no other directories other than the ones ignored? ```mv -f``` does not seem to work. I've temp solved it by creating a temp directory so there are three dirs in total, then moving all directories, then removing the temp dir. ugly! – Michahell Apr 07 '15 at 14:21
  • Be careful with this one that you really want *everything* in that directory in that other directory! – Noumenon Aug 31 '16 at 15:46
  • Thank you for providing an easy to understand example, I really appreciate it! – Terren Jun 13 '18 at 23:51
  • If needed, `shopt -u extglob` to toggle "off" (view status with `shopt extglob`). – Victoria Stuart Jun 15 '19 at 18:07
  • Note that this **will not work if** you write e.g. `mv !(child1/) grandChild*` - the name of the directory to exclude must not contain the trailing slash! – Jo Mo Feb 15 '21 at 06:35
6

Since find does have an exclude option, use find + xargs + mv:

find /source/directory -name ignore-directory-name -prune -print0 | xargs -0 mv --target-directory=/target/directory

Note that this is almost copied from the find man page (I think using mv --target-directory is better than cpio).

Slartibartfast
  • 1,694
  • 9
  • 8
4

First get the names of files and folders and exclude whichever you want:

ls --ignore=file1 --ignore==folder1 --ignore==regular-expression1 ...

Then pass filtered names to mv as the first parameter and the second parameter will be the destination:

mv $(ls --ignore=file1 --ignore==folder1 --ignore==regular-expression1 ...) destination/
2

This isn't exactly what you asked for, but it might do the job:

mv the-folder-you-want-to-exclude somewhere-outside-of-the-main-tree
mv the-tree where-you-want-it
mv the-excluded-folder original-location

(Essentially, move the excluded folder out of the larger tree to be moved.)

So, if I have a/ and I want to exclude a/b/c/*:

mv a/b/c ../c
mv a final_destination
mkdir -p a/b
mv ../c a/b/c

Or something like that. Otherwise, you might be able to get find to help you.

Thanatos
  • 42,585
  • 14
  • 91
  • 146
  • And if the files you are moving are in your root? Some systems, for instance, only give user access to one directory (some cloud IDEs are a good example). Maybe someone wants to move everything to a child directory. This won't work. – Kallaste Sep 25 '17 at 18:00
  • 1
    As I said (six years ago), the answer doesn't describe exactly what was asked for, and it only *might* do the job. For most cases, it should work; certainly, yours is an exception to that. – Thanatos Sep 25 '17 at 23:00
  • You're right, it does not. But my point was not that it wasn't what was asked for, but that it in some cases would not work. My example was just a hypothetical to demonstrate that. – Kallaste Sep 26 '17 at 04:27
2

rename your directory to make it hidden so the wildcard does not see it:

mv specific_dir .specific_dir 
mv * ../other_dir
marc
  • 355
  • 3
  • 6
1

This will move all files at or below the current directory not in the ./exclude/ directory to /wherever...

find -E . -not -type d -and -not -regex '\./exclude/.*' -exec echo mv {} /wherever \;
sjr
  • 9,769
  • 1
  • 25
  • 36
1
ls | grep -v exclude-dir | xargs -t -I '{}' mv {} exclude-dir
double-beep
  • 5,031
  • 17
  • 33
  • 41
xyz
  • 100
  • 1
  • 4
  • 8
    Would you like to add some explanation to your code-only answer? It would help fighting the misconception that StackOverflow is a free code writing service. Also, have a look here to improve appearance: https://stackoverflow.com/editing-help – Yunnosch May 20 '19 at 06:18
0
#!/bin/bash

touch apple  banana  carrot  dog  cherry

mkdir fruit

F="apple  banana  carrot  dog cherry"

mv ${F/dog/} fruit

# this removes 'dog' from the list F, so it remains in the current directory and not moved to 'fruit'

Chris Reid
  • 460
  • 4
  • 9
0

Inspired by @user13747357 's answer.

First you can ls the file and filter them by:

ls | egrep -v '(dir_name|file_name.ext)'

Then you can run the following command to move the files except the specific ones:

mv $(ls | egrep -v '(dir_name|file_name.ext)') target_dir

* Note that I tested this inside a specific directory. Cross-directory operation should be more carefully executed :)

kyon45
  • 33
  • 4
0

suppose you directory is

.
├── dir1
│    └── a.txt
├── dir2
│    ├── b.txt
│    └── hello.c
├── file1.txt
├── file2.txt
└── file3.txt

and you gonna put file1 file2 file3 into dir2.

you can use mv $(ls -p | grep -v /) /dir2 to finish it, because

ls -p | grep -v / will print all files except directory in cwd.

Tracker647
  • 29
  • 6
0

For example, if I want to move all files/directories - except a specified file or directory - inside "var/www/html" to a sub-folder named "my_sub_domain", then I use "mv" with the command "!(what_to_exclude)":

$ cd /var/www/html $ mv !(my_sub_domain) my_sub_domain

To exclude more I use "|" to seperate file/directory names:

$ mv !(my_sub_domain|test1.html) my_sub_domain

Legion
  • 90
  • 11
-1
mv * exclude-dir

was the perfect solution for me