如何将所有文件(包括隐藏文件)从一个目录移动到另一个目录?


132

如何将目录中的所有文件(包括隐藏文件)移动到另一个目录?

例如,如果我有一个文件夹“ Foo”,其中包含文件“ .hidden”和“ notHidden”,如何将这两个文件都移动到名为“ Bar”的目录中?由于“ .hidden”文件保留在“ Foo”中,因此以下内容不起作用。

mv Foo/* Bar/

自己尝试。

mkdir Foo
mkdir Bar
touch Foo/.hidden
touch Foo/notHidden
mv Foo/* Bar/


ah,我知道我应该在输入我的答案之前先刷新一下答案。非常有用的链接。
史蒂文·D

可悲的是,这个问题在这里结束了,因为我在SO上说应该移到这里或SU,所以当SU上已经有副本时,它当然移到这里了:)
Michael Mrozek

就我个人而言,我认为* nix特定的问题和答案应该在SU上不合时宜。按照当前的规则,我们最终会在两者之间产生大量的内容冲突-就像这个问题一样。
科里·克莱恩

Answers:


151

sh

mv Foo/*(DN) Bar/

要么

setopt -s glob_dots
mv Foo/*(N) Bar/

(N)如果知道目录不为空,请忽略。)

重击

shopt -s dotglob nullglob
mv Foo/* Bar/

93号

如果您知道目录不为空:

FIGNORE='.?(.)'
mv Foo/* Bar/

标准(POSIX)sh

for x in Foo/* Foo/.[!.]* Foo/..?*; do
  if [ -e "$x" ]; then mv -- "$x" Bar/; fi
done

如果您愿意让mv命令即使成功执行也返回错误状态,则简单得多:

mv Foo/* Foo/.[!.]* Foo/..?* Bar/

GNU find和GNU mv

find Foo/ -mindepth 1 -maxdepth 1 -exec mv -t Bar/ -- {} +

标准查找

如果您不介意切换到源目录:

cd Foo/ &&
find . -name . -o -exec sh -c 'mv -- "$@" "$0"' ../Bar/ {} + -type d -prune

这是有关控制bash,ksh93和zsh中的点文件是否匹配的更多详细信息。

重击

设置dotglob选项

$ echo *
none zero
$ shopt -s dotglob
$ echo *
..two .one none zero

还有一个更灵活的GLOBIGNORE变量,您可以将其设置为以冒号分隔的通配符模式列表,以将其忽略。如果未设置(默认设置),则外壳程序的行为就像设置了该值时为空dotglob.*如果该选项未设置则为该值。请参见手册中的“ 文件名扩展 ”。除非目录.与模式明确匹配..,否则将始终省略普遍目录和.

$ GLOBIGNORE='n*'
$ echo *
..two .one zero
$ echo .*
..two .one
$ unset GLOBIGNORE
$ echo .*
. .. ..two .one
$ GLOBIGNORE=.:..
$ echo .*
..two .one

93号

设置FIGNORE变量。如果未设置(默认设置),则外壳的行为就像该值是.*。要忽略...,必须对其进行显式匹配(ksh 93s + 2008-01-31中的手册指出...始终被忽略,但这不能正确描述实际行为)。

$ echo *
none zero
$ FIGNORE='@(.|..)'
$ echo *
..two .one none zero
$ FIGNORE='n*'
$ echo *
. .. ..two .one zero

您可以通过明确匹配点文件来将它们包括在模式中

$ unset FIGNORE
$ echo @(*|.[^.]*|..?*)
..two .one none zero

要使扩展名在目录为空的情况下为空,请使用N模式匹配选项:~(N)@(*|.[^.]*|..?*)~(N:*|.[^.]*|..?*)

sh

设置dot_glob选项

% echo *
none zero
% setopt dot_glob
% echo *
..two .one none zero

.并且..永远不会匹配,即使模式.明确匹配开头。

% echo .*
..two .one

您可以使用D glob限定符以特定模式包含点文件。

% echo *(D)
..two .one none zero

添加Nglob限定词以使扩展在一个空目录中为空:*(DN)


注意:您可能会在不同的顺序文件名扩展的结果(例如,none接着.one后面..two)根据您的设置LC_COLLATELC_ALL以及LANG变量。


1
为什么nullglob呢 与运行没有意义的命令相比,中止mv命令(例如fish,csh / tcsh,zsh(无(N))do或使用failglob进行bash)更有mv /Bar/意义。
斯特凡Chazelas

1
我会说您对GLOBIGNORE的描述不准确且具有误导性。设置GLOBIGNORE一个非空值上dotglob打开,原本...(在其他合理的炮弹一样的zsh,鱼,pdksh程序和衍生品等)永远不会匹配即使您关闭dotglob背过之后。(GLOBIGNORE=:; shopt -u dotglob; echo .*不会输出...)。将GLOBIGNORE设置为.:...:具有相同的效果。
斯特凡Chazelas

ksh93总是忽略.并且..似乎在ksh93k+(它起作用的地方)和ksh93m(不再起作用的地方)之间被打破了。请注意,这样做很糟糕,因为它ksh93会从环境中获取FIGNORE的值,因此FIGNORE='!(..)',例如env var可能会造成破坏。
斯特凡Chazelas

24
#!/bin/bash

shopt -s dotglob
mv Foo/* Bar/

man bash

dotglob(如果设置),bash包含以“。”开头的文件名。扩展路径名的结果。


需要注意的是,如果目录为空,则此命令将返回错误代码(即使该命令实际上按预期执行)。
Gilles

1
@Gilles,则错误将归因于mv抱怨所调用的文件Foo/*不存在。有趣的是,如果Foo可搜索但不可读,并且其中有一个文件被调用*,则不会出错,该文件将被移动,但目录中的其他文件不会被移动。bash有一个failglob选项,因此它的行为更像zshfishcsh/,tcsh并且当无法扩展glob而不是将glob保持原样的虚假行为时,中止命令并显示错误。
斯特凡Chazelas

7

一个简单的方法bash

mv {Foo/*,Foo/.*} Bar/

但这也会移动目录。

如果要移动所有文件(包括隐藏文件),但又不想移动任何目录,则可以使用for循环和测试。

for i in $(ls -d {Foo/*,Foo/.*});do test -f $i && mv -v $i Bar/; done;


4

一种方法是使用find

find Foo/ -type f -exec mv -t Bar/ {} \+

-type f限制将find命令限制为查找文件。你应该调查-type-maxdepth-mindepth选项find来定制你的命令的帐户子目录。查找有一个冗长但非常有用的手册页


3
这样只会将常规文件Foo/移入,而不将子目录和其他文件移入。
吉尔斯


1

Rsync是另一种选择:

rsync -axvP --remove-source-files sourcedirectory/ targetdirectory

之所以起作用,是因为在rsync中尾部的斜杠很重要,它sourcedirectory/指的是目录的内容,而sourcedirectory指目录本身。

这种方法的缺点是rsync仅在移动后清理文件,而不清理目录。因此,您将剩下一个空的源目录树。有关解决方法,请参见:

使用rsync移动文件并删除目录?

因此,尽管这对于移动操作可能不是最佳选择,但对于复制操作而言却非常有用。


1

扑食鱼的答案

这是使用通配符的一种方法:

.[!.]* ..?*将匹配所有隐藏文件,除了...

.[!.]* ..?* *将匹配所有文件(是否隐藏),除了...

并回答这个问题的特定示例 cd foo && mv .[!.]* ..?* * ../bar

说明

.[!.]*匹配以一个点开头的文件名,然后是任何字符,除了该点之外还可以选择后面跟任意字符串。这足够接近,但是会丢失以两个点开头的文件,例如..foo。为了包括此类文件,我们添加..?*了与文件名匹配的文件名,文件名以两个点开头,后跟任意字符,并可选地后跟任意字符串。

测试

您可以使用以下命令测试这些通配符。我已经成功地尝试了它们。他们在sh,zsh,xonsh下失败。

mkdir temp
cd temp
touch  a  .b  .bc  ..c  ..cd  ...d  ...de
ls .[!.]* ..?*
# you get this output:
          .b  .bc  ..c  ..cd  ...d  ...de
# cleanup
cd ..
rm -rf temp

0

您也许还可以找到带有反引号的grep并进行grep选择,以选择move命令的文件。将它们传递到mv。

即对于隐藏文件

find Foo -maxdepth 1 | egrep '^Foo/[.]' # Output: .hidden

所以

mv `find Foo -maxdepth 1 | egrep '^Foo/[.]'` Bar # mv Foo/.hidden Bar

仅将选定的隐藏文件移动到Bar

mv `find Foo -maxdepth 1 | egrep '^Foo/.'` Bar # mv Foo/.hidden Foo/notHidden Bar

从“。”开始将Foo中的所有文件移动到Bar。egrep命令中的命令用作通配符,不带方括号。

^特性可以确保匹配从行的开头开始。

egrep模式匹配的一些细节可以在这里找到。

使用maxdepth 1停止查找进入子目录。


0

这个答案中得到启发:

无需复制文件...

rsync -ax --link-dest=../Foo/ Foo/ Bar

注意事项:

  • --link-dest路径必须是绝对路径或相对于DESTINATION的路径(Bar 在示例中)

  • 您必须将/SOURCE 放在后面(Foo/在示例中),否则它将复制SOURCE文件夹而不是其内容。


0

我发现这对于bash非常有效,无需更改shell选项

mv sourcedir/{*,.[^.]*} destdir/

编辑:

因此,正如G-man所说,我的原始答案不符合posix,并且与ndemou的答案几乎相同,只是作了一次更改,即使用花括号扩展来创建字符串列表,然后对其进行操作。这只是意味着您不需要cd进入源目录。确实变化不大,但是却有所不同。

示例:假设您已经具有以下布局。

 $ tree -a
.
├── destdir
└── sourcedir
    ├── ..d1
    ├── ..d2
    ├── ..double
    ├── file-1
    ├── file-2
    ├── .hidden-1
    ├── .hidden-2
    ├── ...t1
    └── ...t2

最初的问题只提到了带有单个句点的隐藏文件,但让我们说一些在文件名开头带有两个或多个句点的文件。您可以在括号中添加一个附加表达式。然后我们可以执行

mv sourcedir/{*,.[!.]*,..?*} destdir/

这被扩展为以下内容:

mv sourcedir/file-1 sourcedir/file-2 sourcedir/.hidden-1 sourcedir/.hidden-2 sourcedir/..d1 sourcedir/..d2 sourcedir/..double sourcedir/...t1 sourcedir/...t2 destdir/

现在您应该看到位于destdir的所有文件:

 $ tree -a
.
├── destdir
   ├── ..d1
   ├── ..d2
   ├── ..double
   ├── file-1
   ├── file-2
   ├── .hidden-1
   ├── .hidden-2
   ├── ...t1
   └── ...t2
└── sourcedir

您可以在bash中使用花括号来做一些非常酷的事情,在4.x中甚至可以添加更多内容。查看bash黑客以获取一些漂亮的示例。


1
除了您添加了花括号(没有解释它们)之外,这基本上与ndemou的答案重复。但是(1)您的答案与名称开头的文件不匹配..,并且(2)与POSIX兼容,应使用!而不是^; 即  {*,.[!.]*}
G-Man

@ G-Man,对此感到抱歉。因此,您是正确的,而我很遗憾没有看到ndemou的回应。他们几乎是同一回事。1.感谢您指出我的版本不符合posix。2.我正在使用括号扩展来创建字符串列表,然后外壳程序将使用这些字符串执行操作。这也意味着我不需要cd进入源目录来对文件进行操作,也不需要一遍又一遍地写出相同的文件路径来表示所有文件的路径。我将尽快更新示例,以使所有读者都能更好地了解我在说什么。
cmndr sp0ck

0

对于最少的 Linux发行版,以下应该可以工作。首先,执行全部基本移动(丢失所有隐藏文件)。然后,将所有隐藏文件(包括...不会实际移动)。

mv /sourceDir/* /destinationDir 2> /dev/null
mv /sourceDir/.* /destinationDir 2> /dev/null

注意:如果没有可见的内容,则第一个命令将产生一条错误消息。第二个命令总是产生一个错误,说它不能移动...。这样,只需将错误传送到/dev/null(如图所示)。

如果您需要将其作为单线使用,只需将它们与分号结合使用:

mv /sourceDir/* /destinationDir 2> /dev/null; mv /sourceDir/.* /destinationDir 2> /dev/null
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.