views:

1439

answers:

9

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 a Tux.png. !-sign represents a negation. Is there some tool for the job?

+1  A: 

How about you move everything in the folder and then just move Tux.png back?

I can't think of any shell syntax to say "everything except ..." offhand.

David Zaslavsky
+1  A: 

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
don't use "*.*", as that will only move files that have a "." in the name. Just "*" is enough.
Juliano
If you change it to * it will remove your renamed file as well.
John T
@John T: No, the * glob doesn't match files that begin with dot.
Bill Karwin
ah, you are correct. It's been a while, my mistake.
John T
+12  A: 

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

mv ~/Linux/Old/!(Tux.png) ~/Linux/New/
sth
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?
Masi
@UnixBasics, try: ~/Linux/Old/!(Tux.png|Tux1.png|Tux2.png)
Juliano
@Juliano Very cool command! Are there more mathematical operations like OR and XOR? I assume a pipe is for AND.
Masi
@UnixBasis, yes. Take a look at http://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html
Juliano
@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?
Masi
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
+3  A: 
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
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
+3  A: 

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.

Juliano
this works in tcsh as well
Nathan Fellman
+9  A: 

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
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 \;"
jhs
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
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
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 :)
Masi
+1  A: 

Back in the late 1980s, I had a DOS tool that would have done the trick: it was called "no.exe". The syntax was simple: "no Tux.png move * \somefolder". I realize that doesn't help much here, but just in case someone walks through with a time machine...

sblom
A: 

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
A: 

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
Masi