Bash 破坏目录的层次结构?

Bash 破坏目录的层次结构?,bash,Bash,我有一个文件夹,里面有很多文件夹,下面有很多文件夹,等等。在这些最后的文件夹中有一小群文件。我正在尝试将这些文件移动到主文件夹并删除现在为空的文件夹层次结构。这就是我目前所拥有的 #!/bin/bash NAME=`whoami` DEST="/Users/"$NAME"/Desktop/Music 2" FILES=`find "$DEST" -type f` for F in "$FILES" do mv "${F}" "${DEST}" done 如果我将

我有一个文件夹,里面有很多文件夹,下面有很多文件夹,等等。在这些最后的文件夹中有一小群文件。我正在尝试将这些文件移动到主文件夹并删除现在为空的文件夹层次结构。这就是我目前所拥有的

#!/bin/bash
NAME=`whoami`
DEST="/Users/"$NAME"/Desktop/Music 2"
FILES=`find "$DEST" -type f`
for F in "$FILES"
    do
        mv "${F}" "${DEST}"
    done
如果我将
mv
命令替换为“echo”,它将捕获所有正确的名称,但当我运行此命令时,它会给我一个错误,说明名称太长。非常感谢您的帮助

所以说我有

 /foo/bar/in/side/test1.txt
 /foo/bar/in/down/test2.doc
 /foo/bar/last/dog/test3.mp3
我希望
test1.txt
test2.doc
、和
test3.mp3
位于
/foo
,以及每个(现在为空)目录
/foo/bar
/foo/bar/in
/foo/bar/in/side
/foo/bar/in/down
/foo/bar/last
,以及
/foo/bar/last/dog>被删除

最终结果:

 /foo/test1.txt
 /foo/test2.doc
 /foo/test3.mp3
尝试这样做:

find "$DEST" -type f -exec bash -c '
    mv "$1" "$DEST"; rmdir "${1%/*}" &>/dev/null
' -- {} \;

尤其是当路径名中有空格时,使用
FILES=$(find…
确实不起作用。您收到的文件名消息太长,因为for循环中的
“$FILES”
将所有名称视为单个文件名
${F}
包含所有内容,
mv
命令试图将单个文件移动到
${DEST}

该命令将删除空目录(请考虑“prune”的
-p
),工作深度优先

GNU
mv
有一个非常有用的选项
-t target
用于此上下文:

DEST=/foo
find /foo/bar -type f -exec mv -t "${DEST}" {} +
find /foo/bar -type d -depth -exec rmdir -p {} +
考虑到您使用的是Mac OS X,您并没有那么方便,因此您最好的选择是速度较慢(但同样有效):

这会对每个文件执行一次
mv
命令(而对于GNU
mv
,许多文件可以通过一次调用进行移动)。否则,它是等价的

这两组命令都避免了文件名中的空格问题

请注意,如果
$DEST
与正在搜索的目录相同,则将
$DEST
中已有的文件移到自己的上方时会遇到问题。正如所写的,代码并没有避免这个问题。如有必要,您可以通过以下方式避免:

find "$DEST"/*/ -type f ...
尾随斜杠强制执行“仅目录”(将其视为等同于“
”$DEST”/*/。


概念验证脚本 记住:除非你们手头有好的备份,否则一定要在现场材料的副本上测试破坏性脚本(删除内容的脚本)。事实上,无论如何都要复制它们;制作拷贝几乎总是比从备份中恢复要快(但如果数据至关重要,无论如何都应该有备份)

rmdir-p
进程有噪声。它报告无法删除的目录。GNU版本的
rmdir
提供了一个抑制某些错误的选项(
--ignore fail on non-empty
),但在上下文中,您也会遇到一些关于不存在目录的错误(在单独列出目录的条目之前,通过剪枝过程将其删除)。所以,在忍受了一点噪音之后,我将所有错误从
rmdir
重定向到
/dev/null
。删除该重定向,直到您满意事情按预期工作为止

此脚本应在您刚刚创建并设置为当前目录的空目录中运行:

mkdir junk
cd junk
cp ../script .
sh -x ./script
样本输出:

$ sh -x script
+ echo Before
Before
+ du -a .
4   ./script
8   .
+ filelist='./foo/bar/in/side/test1.txt
          ./foo/bar/in/down/test2.doc
          ./foo/bar/last/dog/test3.mp3'
+ for file in '$filelist'
++ dirname ./foo/bar/in/side/test1.txt
+ mkdir -p ./foo/bar/in/side
+ cp script ./foo/bar/in/side/test1.txt
+ for file in '$filelist'
++ dirname ./foo/bar/in/down/test2.doc
+ mkdir -p ./foo/bar/in/down
+ cp script ./foo/bar/in/down/test2.doc
+ for file in '$filelist'
++ dirname ./foo/bar/last/dog/test3.mp3
+ mkdir -p ./foo/bar/last/dog
+ cp script ./foo/bar/last/dog/test3.mp3
+ FIFO=./foo/bar/first/installment
++ dirname ./foo/bar/first/installment
+ mkdir ./foo/bar/first
+ mkfifo ./foo/bar/first/installment
+ echo Created
Created
+ du -a .
4   ./foo/bar/in/side/test1.txt
8   ./foo/bar/in/side
4   ./foo/bar/in/down/test2.doc
8   ./foo/bar/in/down
20  ./foo/bar/in
4   ./foo/bar/last/dog/test3.mp3
8   ./foo/bar/last/dog
12  ./foo/bar/last
0   ./foo/bar/first/installment
4   ./foo/bar/first
40  ./foo/bar
44  ./foo
4   ./script
52  .
+ echo 'Clean up'
Clean up
+ DEST=./foo
+ find ./foo/bar -type f -exec mv '{}' ./foo ';'
+ find ./foo/bar -depth -type d -exec rmdir -p '{}' +
+ echo After
After
+ du -a .
0   ./foo/bar/first/installment
4   ./foo/bar/first
8   ./foo/bar
4   ./foo/test1.txt
4   ./foo/test3.mp3
4   ./foo/test2.doc
24  ./foo
4   ./script
32  .
+ rm -fr ./foo
请注意,此脚本在
/foo/bar
下的单独目录中小心地创建了一个非文件(FIFO),并显示它被遗忘了。注释掉创建FIFO的
mkfifo
行,运行如下所示:

$ sh -x script
+ echo Before
Before
+ du -a .
4   ./script
8   .
+ filelist='./foo/bar/in/side/test1.txt
          ./foo/bar/in/down/test2.doc
          ./foo/bar/last/dog/test3.mp3'
+ for file in '$filelist'
++ dirname ./foo/bar/in/side/test1.txt
+ mkdir -p ./foo/bar/in/side
+ cp script ./foo/bar/in/side/test1.txt
+ for file in '$filelist'
++ dirname ./foo/bar/in/down/test2.doc
+ mkdir -p ./foo/bar/in/down
+ cp script ./foo/bar/in/down/test2.doc
+ for file in '$filelist'
++ dirname ./foo/bar/last/dog/test3.mp3
+ mkdir -p ./foo/bar/last/dog
+ cp script ./foo/bar/last/dog/test3.mp3
+ FIFO=./foo/bar/first/installment
++ dirname ./foo/bar/first/installment
+ mkdir ./foo/bar/first
+ echo Created
Created
+ du -a .
4   ./foo/bar/in/side/test1.txt
8   ./foo/bar/in/side
4   ./foo/bar/in/down/test2.doc
8   ./foo/bar/in/down
20  ./foo/bar/in
4   ./foo/bar/last/dog/test3.mp3
8   ./foo/bar/last/dog
12  ./foo/bar/last
4   ./foo/bar/first
40  ./foo/bar
44  ./foo
4   ./script
52  .
+ echo 'Clean up'
Clean up
+ DEST=./foo
+ find ./foo/bar -type f -exec mv '{}' ./foo ';'
+ find ./foo/bar -depth -type d -exec rmdir -p '{}' +
+ echo After
After
+ du -a .
4   ./foo/test1.txt
4   ./foo/test3.mp3
4   ./foo/test2.doc
16  ./foo
4   ./script
24  .
+ rm -fr ./foo
$
这强烈表明,如果编写得当并小心处理,上述代码确实可以正常工作,而不会损坏系统。但在生产中(甚至在测试中)使用任何变体之前,您仍然应该谨慎


测试在Ubuntu 12.04的派生版本上运行。

是否将其替换为mv命令?或者对于整个“for”命令?好吧,我试过了,它破坏了层次结构,但也删除了最后文件夹中的文件。这是每个文件的
rmdir
;如果经常做,有点贵。一旦关闭,对于几(千)个文件来说,这并不重要,但对于生产工作来说,这并不完全令人满意/bin/bash DEST=“/Users/Zelon/Desktop/Music 2”FILES=
查找“$DEST”-键入f
在“$FILES”中查找“$DEST”-键入f-exec bash-c'rm-f“$1”;rmdir“${1%/*}”和>/dev/null'-{}\;如果$DEST目录是我移动它们的地方,那么代码“DEST=/foo find/foo/bar-type f-exec mv{}”${DEST}“;“find/foo/bar-type d-depth-exec rmdir-p{}+”会将它们移动到那个位置吗?是的;move命令将文件移动到
${DEST}
。由于
$DEST
中会有文件,所以
rmdir
看起来要修剪目录并不重要;它不会被删除。你从来没有在实时目录上测试过,是吗?你总是先在拷贝上测试删除操作,不是吗?或者,如果你以前没有,将来也会。这对我有用。我会再次检查,但是…很抱歉,我看到了我在编辑参数代码时弄糟的问题。虽然我能够从以前的备份中恢复应用程序,并修复了我弄糟的脚本,但一切正常。我需要一点时间才能再次运行它。它可能会破坏整个系统的文件。你必须极其小心地使用任何破坏性脚本。当你没有编写和测试每一行的时候,情况就更糟了。我很欣慰,你有备份工作;很抱歉,我没有提醒你采取预防措施。请注意,代码不会同时保留
/foo/bar/in/file9.txt
/foo/bar/last/file9.txt
——它只保留一个,保留哪一个是准任意的。您可能可以修改它来处理名称冲突,但这并不是完全直接的。
$ sh -x script
+ echo Before
Before
+ du -a .
4   ./script
8   .
+ filelist='./foo/bar/in/side/test1.txt
          ./foo/bar/in/down/test2.doc
          ./foo/bar/last/dog/test3.mp3'
+ for file in '$filelist'
++ dirname ./foo/bar/in/side/test1.txt
+ mkdir -p ./foo/bar/in/side
+ cp script ./foo/bar/in/side/test1.txt
+ for file in '$filelist'
++ dirname ./foo/bar/in/down/test2.doc
+ mkdir -p ./foo/bar/in/down
+ cp script ./foo/bar/in/down/test2.doc
+ for file in '$filelist'
++ dirname ./foo/bar/last/dog/test3.mp3
+ mkdir -p ./foo/bar/last/dog
+ cp script ./foo/bar/last/dog/test3.mp3
+ FIFO=./foo/bar/first/installment
++ dirname ./foo/bar/first/installment
+ mkdir ./foo/bar/first
+ mkfifo ./foo/bar/first/installment
+ echo Created
Created
+ du -a .
4   ./foo/bar/in/side/test1.txt
8   ./foo/bar/in/side
4   ./foo/bar/in/down/test2.doc
8   ./foo/bar/in/down
20  ./foo/bar/in
4   ./foo/bar/last/dog/test3.mp3
8   ./foo/bar/last/dog
12  ./foo/bar/last
0   ./foo/bar/first/installment
4   ./foo/bar/first
40  ./foo/bar
44  ./foo
4   ./script
52  .
+ echo 'Clean up'
Clean up
+ DEST=./foo
+ find ./foo/bar -type f -exec mv '{}' ./foo ';'
+ find ./foo/bar -depth -type d -exec rmdir -p '{}' +
+ echo After
After
+ du -a .
0   ./foo/bar/first/installment
4   ./foo/bar/first
8   ./foo/bar
4   ./foo/test1.txt
4   ./foo/test3.mp3
4   ./foo/test2.doc
24  ./foo
4   ./script
32  .
+ rm -fr ./foo
$ sh -x script
+ echo Before
Before
+ du -a .
4   ./script
8   .
+ filelist='./foo/bar/in/side/test1.txt
          ./foo/bar/in/down/test2.doc
          ./foo/bar/last/dog/test3.mp3'
+ for file in '$filelist'
++ dirname ./foo/bar/in/side/test1.txt
+ mkdir -p ./foo/bar/in/side
+ cp script ./foo/bar/in/side/test1.txt
+ for file in '$filelist'
++ dirname ./foo/bar/in/down/test2.doc
+ mkdir -p ./foo/bar/in/down
+ cp script ./foo/bar/in/down/test2.doc
+ for file in '$filelist'
++ dirname ./foo/bar/last/dog/test3.mp3
+ mkdir -p ./foo/bar/last/dog
+ cp script ./foo/bar/last/dog/test3.mp3
+ FIFO=./foo/bar/first/installment
++ dirname ./foo/bar/first/installment
+ mkdir ./foo/bar/first
+ echo Created
Created
+ du -a .
4   ./foo/bar/in/side/test1.txt
8   ./foo/bar/in/side
4   ./foo/bar/in/down/test2.doc
8   ./foo/bar/in/down
20  ./foo/bar/in
4   ./foo/bar/last/dog/test3.mp3
8   ./foo/bar/last/dog
12  ./foo/bar/last
4   ./foo/bar/first
40  ./foo/bar
44  ./foo
4   ./script
52  .
+ echo 'Clean up'
Clean up
+ DEST=./foo
+ find ./foo/bar -type f -exec mv '{}' ./foo ';'
+ find ./foo/bar -depth -type d -exec rmdir -p '{}' +
+ echo After
After
+ du -a .
4   ./foo/test1.txt
4   ./foo/test3.mp3
4   ./foo/test2.doc
16  ./foo
4   ./script
24  .
+ rm -fr ./foo
$