198

How can I move all files except one? I am looking for something like:

'mv ~/Linux/Old/!Tux.png ~/Linux/New/'

where I move old stuff to new stuff -folder except Tux.png. !-sign represents a negation. Is there some tool for the job?

Costique
  • 23,712
  • 4
  • 76
  • 79
Léo Léopold Hertz 준영
  • 134,464
  • 179
  • 445
  • 697

14 Answers14

174

If you use bash and have the extglob shell option set (which is usually the case):

mv ~/Linux/Old/!(Tux.png) ~/Linux/New/
sth
  • 222,467
  • 53
  • 283
  • 367
  • 1
    I got something wrong when I tested the command for many items: mv ~/Linux/Old/!(Tux.png Tux1.png Tux2.png) ~/Linux/New/ It empties the whole Old -directory. What is wrong? – Léo Léopold Hertz 준영 Mar 22 '09 at 03:28
  • 11
    @UnixBasics, try: ~/Linux/Old/!(Tux.png|Tux1.png|Tux2.png) – Juliano Mar 22 '09 at 03:32
  • @Juliano Very cool command! Are there more mathematical operations like OR and XOR? I assume a pipe is for AND. – Léo Léopold Hertz 준영 Mar 22 '09 at 03:39
  • @UnixBasis, yes. Take a look at http://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html – Juliano Mar 22 '09 at 04:07
  • @Juliano I am rather confused of the patterns like: mv ~/Linux/?(Tux*.png) ~/Linux/New/ mv ~/Linux/+(Tux*.png) ~/Linux/New/ In the former, I wanted to move 0-1 Tux -photo. In the latter, I wanted move 1-n Tux photos. They moved everyhing. Why? Is it possible to move a specific amount of photos? – Léo Léopold Hertz 준영 Mar 22 '09 at 14:31
  • A specific amount? No AFAIK. You must be more clear at which files you have and which ones you want to move, otherwise it is difficult to say what command you have to type. Note that these patterns are regular expressions, they are executed over each filename, not the other way around. – Juliano Mar 22 '09 at 15:14
  • Such a configuration (extglob) or an equivalent seems to be default for Mac Terminal with "command line tools" installed, as this syntax worked perfectly. – Adrian M. Oct 02 '16 at 03:24
  • 4
    For ZSH user, instead of using `shopt` (which will give `command not found` error), add this to your `.zshrc`: `setopt extended_glob` then the [syntax for glob](http://zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Operators) will also change accordingly. Thus use `mv ~/path/to/source^(exception) ~/path/to/target/folder` should do – Alex Xiong Jan 14 '19 at 03:41
119

Put the following to your .bashrc

shopt -s extglob

It extends regexes. You can then move all files except one by

mv !(fileOne) ~/path/newFolder

Exceptions in relation to other commands

Note that, in copying directories, the forward-flash cannot be used in the name as noticed in the thread Why extglob except breaking except condition?:

cp -r !(Backups.backupdb) /home/masi/Documents/

so Backups.backupdb/ is wrong here before the negation and I would not use it neither in moving directories because of the risk of using wrongly then globs with other commands and possible other exceptions.

Community
  • 1
  • 1
Léo Léopold Hertz 준영
  • 134,464
  • 179
  • 445
  • 697
  • 3
    This is actually the correct answer. Mine was flat out wrong; luckily I had used it in a folder with only few files. Here is more information about the method Masi shows: http://wiki.bash-hackers.org/syntax/pattern Go to "Extended pattern language" and you will find more info on this. Thanks to @paul-whittaker for pointing at the issue. – mimoralea Jul 23 '14 at 16:40
  • This is an old answer but it's still valid. I'm using Ubuntu 18 and extglob seems to be enabled by default (I didn't shopt it). I used the following command to move all files in the current directory into an archive that is also within this directory, NOT including any other archive folders: `mv !(arc*) archive_190629b` – TonyG Jun 29 '19 at 22:03
79

I would go with the traditional find & xargs way:

find ~/Linux/Old -maxdepth 1 -mindepth 1 -not -name Tux.png -print0 | 
    xargs -0 mv -t ~/Linux/New

-maxdepth 1 makes it not search recursively. If you only care about files, you can say -type f. -mindepth 1 makes it not include the ~/Linux/Old path itself into the result. Works with any filenames, including with those that contain embedded newlines.

One comment notes that the mv -t option is a probably GNU extension. For systems that don't have it

find ~/Linux/Old -maxdepth 1 -mindepth 1 -not -name Tux.png \
    -exec mv '{}' ~/Linux/New \;
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 2
    Upvoted since it encourages learning a powerful tool, find. Note, for files with whitespace this wont' work. Consider "find -print0 | xargs -0" or else forego xargs: "find [what you said] -exec mv -t {} ~/Linux/New \;" – JasonSmith Mar 22 '09 at 04:55
  • 1
    If your mv command doesn't have the -t option, then either use "find ... -exec mv {} Linux/New \;" or "find ... | xargs -I {} mv {} Linux/New". The -t option seems to be a GNU extension, which is fine for Linux, but probably not elsewhere. – Rob Kennedy Mar 22 '09 at 05:08
  • jhs, oh i fail. i somehow thought xargs splitted at newlines but not at spaces. looks like i confused it with "read" :) ill fix it – Johannes Schaub - litb Mar 22 '09 at 05:31
  • Great thanks for the awesome tip! I was going to choose sth because it targets the question. However, I can sense the usefulness of your commands later on, so I must choose "find and xargs" way. It just rocks. Thanks :) – Léo Léopold Hertz 준영 Mar 26 '09 at 01:41
  • Way to complicated. Masi answer is more complete, easier and therefor more correct – user637338 Aug 04 '16 at 10:21
  • @user637338 all of your measures are subjective, except "correct". To conclude the objective from subjectives doesn't seem correct either. My take on level of complication: `find` should be your daily bread and butter on linux, and then my `find` line reads almost like natural language. – Johannes Schaub - litb Aug 04 '16 at 10:50
  • I just wanted to give justification why I rated your answer down and Masi's up. Simplicity even in nature should be one of the indicators for good solutions. I think this we can agree on. The question then remains, what simplicity means for different types of people. Comparing your answer with this: "mv !(fileOne) ~/path/newFolder", I would say this in terms of simplicity wins. This is what I wanted to state, maybe that was not clear. – user637338 Aug 04 '16 at 15:39
  • @user637338 I think the comparison is unfair :) It should be compared with "shopt -s extglob; mv !(fileOne) ~/path/newFolder" – Johannes Schaub - litb Aug 04 '16 at 15:43
  • No it is not unfair as you have to enable this once and then you are able to use a nice and short command. The command in this answer is always as long, maybe beautiful to some, but none the less always long. – user637338 Aug 05 '16 at 09:14
  • Be very careful with the FROM parameter (1st parameter) of this command. If you set it to the root of your system, you will be screwed. You will move all your files - flattening their structure, and wrecking your existing folder structure, with no simple way to restore. – FranticRock May 22 '19 at 16:05
37

I think the easiest way to do is with backticks

mv `ls -1 ~/Linux/Old/ | grep -v Tux.png` ~/Linux/New/

Edit:

Use backslash with ls instead to prevent using it with alias, i.e. mostly ls is aliased as ls --color.

mv `\ls -1 ~/Linux/Old/ | grep -v Tux.png` ~/Linux/New/

Thanks @Arnold Roa

Rohail Abbas
  • 551
  • 1
  • 7
  • 20
36

A quick way would be to modify the tux filename so that your move command will not match.

For example:

mv Tux.png .Tux.png

mv * ~/somefolder

mv .Tux.png Tux.png
John T
  • 23,735
  • 11
  • 56
  • 82
17

For bash, sth answer is correct. Here is the zsh (my shell of choice) syntax:

mv ~/Linux/Old/^Tux.png ~/Linux/New/

Requires EXTENDED_GLOB shell option to be set.

Community
  • 1
  • 1
Juliano
  • 39,173
  • 13
  • 67
  • 73
15

I find this to be a bit safer and easier to rely on for simple moves that exclude certain files or directories.

ls -1 | grep -v ^$EXCLUDE | xargs -I{} mv {} $TARGET
Abdel Raoof Olakara
  • 19,223
  • 11
  • 88
  • 133
alex.pilon
  • 512
  • 6
  • 18
7

This could be simpler and easy to remember and it works for me.

mv $(ls ~/folder | grep -v ~/folder/exclude.png) ~/destination

Pakpoom Tiwakornkit
  • 2,601
  • 1
  • 20
  • 19
  • Very useful. `-v` means `--invert-match ` I think for multiple terms it may be possible to use `grep -e` but have not yet worked out how. – cardamom Jul 01 '22 at 16:13
  • 1
    I tried to generalise this for a longer list in the `grep -v` section and it became very complicated and had to ask for help. Interesting and useful solution however https://unix.stackexchange.com/questions/708296/detailed-bash-script-with-cp-and-grep-will-not-work-with-spaces-in-filenames – cardamom Jul 02 '22 at 07:58
6

The following is not a 100% guaranteed method, and should not at all be attempted for scripting. But some times it is good enough for quick interactive shell usage. A file file glob like

[abc]*

(which will match all files with names starting with a, b or c) can be negated by inserting a "^" character first, i.e.

[^abc]*

I sometimes use this for not matching the "lost+found" directory, like for instance:

mv /mnt/usbdisk/[^l]* /home/user/stuff/.

Of course if there are other files starting with l I have to process those afterwards.

hlovdal
  • 26,565
  • 10
  • 94
  • 165
5

This can bei done without grep like this:

ls ~/Linux/Old/ -QI Tux.png | xargs -I{} mv ~/Linux/Old/{} ~/Linux/New/

Note: -I is a captial i and makes the ls command ignore the Tux.png file, which is listed afterwards.

The output of ls is then piped into mv via xargs, which allows to use the output of ls as source argument for mv.

ls -Q just quotes the filenames listed by ls.

ArchLinuxTux
  • 840
  • 1
  • 11
  • 28
5

How about:

mv $(echo * | sed s:Tux.png::g) ~/Linux/New/

You have to be in the folder though.

antibus
  • 983
  • 1
  • 11
  • 16
4
mv `find Linux/Old '!' -type d | fgrep -v Tux.png` Linux/New

The find command lists all regular files and the fgrep command filters out any Tux.png. The backticks tell mv to move the resulting file list.

David Norman
  • 19,396
  • 12
  • 64
  • 54
  • 4
    Never, ever, evaluate find as arguments for another command. Bad things will happen. You may have just created a vulnerability. Always use the find -print0 | xargs -0 construct, or find -exec. – Juliano Mar 22 '09 at 15:09
3
ls ~/Linux/Old/ | grep -v Tux.png | xargs -i {} mv ~/Linux/New/'
Léo Léopold Hertz 준영
  • 134,464
  • 179
  • 445
  • 697
aitor
  • 61
  • 3
  • 1
    This command seems missing one part and doesn't work in my computer. `ls ~/Linux/Old/ | grep -v Tux.png | xargs -I{} mv {} ~/Linux/New/` works instead. – Katherine Chen May 24 '19 at 06:26
0

move all files(not include except file) to except_file
find -maxdepth 1 -mindepth 1 -not -name except_file -print0 |xargs -0 mv -t ./except_file
for example(cache is current except file)
find -maxdepth 1 -mindepth 1 -not -name cache -print0 |xargs -0 mv -t ./cache

mutong
  • 31
  • 1