考虑:
$ ls
foo xyfooz.tex
$ find . -maxdepth 1 -type f -name '*foo*' -exec mv {} foo \;
$ ls ./foo*
xyfooz.tex
我试图仅使用 mv 而不是 find 来实现相同的指令序列:
$ ls
foo xyfooz.tex
$ mv *foo* foo
mv: cannot stat '*foo*': No such file or directory
$ ls ./foo*
xyfooz.tex
另外(编辑),
$ ls -F
foo/ xyfooz.tex*
$ mv -v *foo* foo
mv: cannot move 'foo' to a subdirectory of itself, 'foo/foo'
'xyfooz.tex' -> 'foo/xyfooz.tex'
如何确保不出现“stat”警告。换句话说,我想我应该改进模式以将源限制为文件而不是目录?
GNU bash,4.3.48(1)-发布(x86_64-pc-linux-gnu)
$ cat /etc/os-release
NAME="Linux Mint"
VERSION="18.3 (Sylvia)"
答案1
和mv
cannot move 'foo' to a subdirectory of itself
在这种情况下是无害的,您可以忽略它。我的意思是mv
尽管有警告,但仍会移动其余部分。但是退出状态不会是0
,这可能是脚本中的巨大障碍,因为如果您想要中止或采取纠正措施(如果mv
不成功),这可能会成为一大障碍。
这已经在评论中说:您可以mv
通过使用 重定向 stderr 来静音2>/dev/null
;其他警告或错误也将被重定向。
我认为您不能轻易“改进模式以将源限制为文件而不是目录”。您仍然可以采取与文字不匹配的方法foo
。以下方法与文件或目录无关;只与名称有关,因为 glob 处理名称;因此在某些情况下可能还不够。
glob*foo*
匹配四种分离类型的对象:
foo
本身- foo 只带前缀,例如
bar-foo
– 你可以通过以下方式匹配它们*?foo
- 带有前缀和后缀的 foo,例如
bar-foo-baz
–*?foo?*
- foo 仅带后缀,例如
foo-baz
–foo?*
要排除,foo
只需要后三个(2-4),因此第一种方法可能是:
mv *?foo *?foo?* foo?* foo/
除非你nullglob
设置了 shell 选项,否则任何模式都需要匹配某些内容,否则它将被mv
逐字传递。你不希望这样。解决方案是事先运行以下命令:
shopt -s nullglob
此 shell 选项将使任何不匹配的 glob 扩展为无。我建议您dotglob
也研究一下该选项。
请注意*?foo*
匹配(2-3)。同样*foo?*
匹配(3-4)。另外请考虑--
告知mv
所有后续参数都不是选项。这样,像这样的文件--foo
就不会被解释为(未知)选项。这导致两个更好的命令:
mv -- *?foo* foo?* foo/
或者
mv -- *?foo *foo?* foo/
选择哪一个都没关系。只是不要使用mv *?foo* *foo?* foo/
;它会将(例如)传递bar-foo-baz
给mv
两次(即作为两个单独的参数),从而当工具第二次尝试移动此对象时会产生错误。
当根本找不到匹配项时,我的mv
命令将退化mv foo/
并抛出错误。您的原始mv
命令(nullglob
未设置)将获取文字*foo*
并抛出另一个错误。
控制台会话示例:
$ 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'
$
简单破解
就假设dummy
不存在吧。
mv foo dummy
mv *foo* dummy/
mv dummy foo
在基于 inode 的文件系统中,重命名目录应该非常快。已foo/
打开文件的程序不应干扰或中断,因为 inode 才是最重要的。但是,如果任何程序需要打开foo/
第一个和第三个之间的文件(通过其路径包括)mv
,它将失败。可能会发生其他副作用(想象一下在此期间重新创建第三方程序foo/
),这就是为什么我将这种方法称为 hack。使用它之前请三思。
无论如何find
区分文件和目录是 的工作find
。你用 做的事情find
基本上是正确的。你想用 做的事情(以及我上面做的事情)和mv
shell 通配符似乎……嗯,最多“不太正确”。这是你的命令:
find . -maxdepth 1 -type f -name '*foo*' -exec mv {} foo \;
这find
将为每个匹配的文件运行一个单独的mv
,整个命令的性能会很差。因此,您可能希望切换到单个mv
。如果这是您的思路(并且您的mv
和find
足够丰富),那么您应该考虑这种“非常正确”的方式:
find . -maxdepth 1 -type f -name '*foo*' -exec mv -t foo/ -- {} +
find
与你的命令和/或使用 glob 相比,其优势mv
如下:
- 一个人
mv
可能会得到多个文件来处理; - 但是,如果文件太多,一个命令行无法处理,
find
则会运行额外的mv
进程; - 如果没有文件匹配该模式,
mv
则根本不会运行; - 由于
--
像这样的文件--foo
不会被解释为(未知)选项mv
;