With mv
cannot move 'foo' to a subdirectory of itself
is harmless in this case, you can ignore it. I mean mv
will move the rest despite the warning. However the exit status will not be 0
, this may be a great obstacle in scripts where you want to abort or to take a correcting action if mv
doesn't succeed.
This has been already said in a comment: you can silence mv
by redirecting stderr with 2>/dev/null
; other warnings or errors will be redirected as well though.
I think you cannot easily "refine the pattern to limit the source to files not directories". Still you can take an approach that doesn't match the literal foo
. The following approach has nothing to do with files or directories; just with names, because globs deal with names; so it may not be enough in some cases.
The *foo*
glob matches four disjunctive kinds of objects:
foo
itself
- foo with prefix only, like
bar-foo
– you can match them by *?foo
- foo with prefix and postfix, like
bar-foo-baz
– *?foo?*
- foo with postfix only, like
foo-baz
– foo?*
To exclude foo
you need the latter three (2-4) only, so the first approach may be:
mv *?foo *?foo?* foo?* foo/
Unless you have the nullglob
shell option set, any pattern needs to match something, otherwise it will be passed to mv
literally. You don't want this. The solution is to run the following command beforehand:
shopt -s nullglob
This shell option will make any unmatched glob expand to nothing. I advise you to research dotglob
option as well.
Note that *?foo*
matches (2-3). Similarly *foo?*
matches (3-4). Additionally consider --
to tell mv
that all following arguments aren't options. This way a file like --foo
won't be interpreted as an (unknown) option. This leads to two better commands:
mv -- *?foo* foo?* foo/
or
mv -- *?foo *foo?* foo/
It doesn't matter which one you choose. Just don't use mv *?foo* *foo?* foo/
; it will pass (e.g.) bar-foo-baz
to mv
twice (i.e. as two separate arguments), thus generating an error when the tool tries to move this object for the second time.
When no match is found at all, my mv
commands will degenerate themselves to mv foo/
and throw an error. Your original mv
command (with nullglob
unset) would get the literal *foo*
and throw another error.
Example console session:
$ shopt -s nullglob
$ mkdir foo
$ touch bar-foo bar-foo-baz foo-baz xyfooz.tex ./--foo
$ mv -v -- *?foo* foo?* foo/
'bar-foo' -> 'foo/bar-foo'
'bar-foo-baz' -> 'foo/bar-foo-baz'
'--foo' -> 'foo/--foo'
'xyfooz.tex' -> 'foo/xyfooz.tex'
'foo-baz' -> 'foo/foo-baz'
$
Simple hack
Let's say dummy
doesn't exist.
mv foo dummy
mv *foo* dummy/
mv dummy foo
Renaming a directory should be very fast in inode-based filesystems. Programs having files from within foo/
already opened shouldn't interfere nor break because it's the inode that matters. However if any program needs to open a file (by its path including foo/
) between the first and the third mv
, it will fail. Other side effects may occur (imagine a third party program recreating foo/
in the meantime), that's why I call this approach a hack. Think twice before you use it.
With find
anyway
Telling apart files from directories is a job for find
. What you did with find
is basically the right way. What you want to do (and what I did above) with mv
and shell globbing seems… well, "less right" at most. This is your command:
find . -maxdepth 1 -type f -name '*foo*' -exec mv {} foo \;
This find
will run a separate mv
for every matched file, the whole command will perform poorly. For this reason one may want to switch to a single mv
. If this is your line of thought (and your mv
and find
are rich enough) then you should consider this "very right" way:
find . -maxdepth 1 -type f -name '*foo*' -exec mv -t foo/ -- {} +
The advantages over your find
command and/or over plain mv
with glob(s):
- a single
mv
may get multiple files to work with;
- still, if there are too many files for one command line to handle,
find
will run additional mv
process(es);
- in a case when no file matches the pattern,
mv
won't be run at all;
- thanks to
--
a file like --foo
will not be interpreted as an (unknown) option to mv
;
find
orfor f in *foo*; do [ -f "$f" ] && mv "$f" foo; done
. Please update your question with the results on repeating both tests usingls -F
each time andmv -v
in both thefind ... -exec
and the stand-alone command. I'm using Ubuntu 16.04.04 withGNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
. Iffoo
is a directory andxyfooz.tex
a file, my stand-alonemv
givesmv: cannot move 'foo' to a subdirectory of itself, 'foo/foo'
, which is as expected. What OS are you using?