9

I have this situation where I want to exclude a directory while using the mv command. I saw this example where the syntax would be !(exclude_dir), but when I set up a scenario to test it I get results that I don't fully understand.

Now I have created three folders: f1,f2 and f3. Now I use the command in this way:

mv -t f3/ !(f1) f2

This produces this error:

mv: cannot move 'f3' to a subdirectory of itself, 'f3/f3'
mv: cannot stat 'f2': No such file or directory

Now funny thing is the structure of the folder is now:

.
├── f1
└── f3
    └── f2

3 directories, 0 files

It does what I want but why the error messages. Obviously I am not using that command correctly.

0

4 Answers 4

12

This doesn't have anything to do with mv, but is a bash feature, citing man bash:

If the extglob shell option is enabled using the shopt builtin, several extended pattern matching operators are recognized. In the following description, a pattern-list is a list of one or more patterns separated by a |. Composite patterns may be formed using one or more of the following sub-patterns:

!(pattern-list)
Matches anything except one of the given patterns

!(f1) matches f2 f3 in your example, so effectively you're doing:

mv -t f3/ f2 f3 f2

Then, it successfully moves f2 to f3, raises the first error trying to move f3 to itself, and raises the second error when trying to move f2 to f3 again because it was already moved and is no longer in the current directory.

To achieve your goal you should rather do:

mv -t f3/ !(f[13]) # or !(f1|f3)

This expression matches everything except f1 and f3.

This also works with *, ? and […]:

$ ls
e1  e2  e3  f1  f2  f3
$ ls !(e*|?[12])
f3
4

!(f1) is an extended glob expression, so (provided the extglob shell option is set) it expands to a list of (non)-matching files. In other words, if your directory originally contained f1, f2, f3 then

mv -t f3/ !(f1) f2

expands as

mv -t f3/ f2 f3 f2

The first error should be obvious; the second is because it attempts to move f2 twice - and fails the second time.

0
1

As an alternative you can also use rsync:

rsync -arv --exclude=.ccache --exclude=build /home/ben /media/ben/thumbdrive/

https://askubuntu.com/a/320459/585364

1

If you're looking for a way to backup current files, it would be safer to split the move command into copy & remove. For example:

# Allow wildcard and hidden files search
shopt -s extglob dotglob

# Make a new directory (skip if exists)
mkdir -p BACKUP

# Copy all files to BACKUP (-a to keep attributes, -f to overwrite existing)
cp -af !(BACKUP) BACKUP/

# Remove all files except BACKUP (and hidden files starting with ".keep")
rm -rf !(BACKUP|.keep*)

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .