销毁目录的层次结构?

时间:2013-12-25 19:16:16

标签: 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

如果我用“echo”替换mv命令,它将捕获所有正确的名称,但是当我运行它时,它会给出一个错误,说明名称太长。非常感谢帮助。

所以说我有

 /foo/bar/in/side/test1.txt
 /foo/bar/in/down/test2.doc
 /foo/bar/last/dog/test3.mp3

我希望test1.txttest2.doctest3.mp3位于/foo,并且对于每个(现在为空)目录/foo/bar,{{要删除1}},/foo/bar/in/foo/bar/in/side/foo/bar/in/down/foo/bar/last

最终结果:

/foo/bar/last/dog

2 个答案:

答案 0 :(得分:1)

尝试这样做:

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

答案 1 :(得分:0)

特别是当路径名中包含空格时,使用FILES=$(find ...)确实不起作用。您的文件名太长了,因为for循环中的"$FILES"将所有名称视为单个文件名; ${F}包含所有内容,mv命令正在尝试将单个文件移至${DEST}

rmdir -p命令删除空的目录(将-p视为'prune'),首先是工作深度。

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,您没有那么方便,所以最好的选择是放慢速度(但同样有效):

DEST=/foo
find /foo/bar -type f -exec mv {} "${DEST}" ';'
find /foo/bar -type d -depth -exec rmdir -p {} +

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

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

请注意,如果$DEST与您正在搜索的目录相同,那么移动$DEST中已有的文件时会遇到问题。如上所述,代码不能避免这个问题。如有必要,您可以通过以下方式避免这种情况:

find "$DEST"/*/ -type f ...

尾部斜杠强制执行“仅目录”(将其视为等同于"$DEST"/*/.)。


概念证明脚本

请记住:除非您手头有好的备份,否则请始终在实时素材的副本上测试破坏性脚本(删除内容的脚本)。实际上,无论如何都要复制它们;制作副本几乎总是比从备份中恢复更快(但如果数据至关重要,你应该有备份)。

echo "Before"
du -a .

filelist="./foo/bar/in/side/test1.txt
          ./foo/bar/in/down/test2.doc
          ./foo/bar/last/dog/test3.mp3"
for file in $filelist
do
    mkdir -p $(dirname $file)
    cp script $file
done

FIFO=./foo/bar/first/installment
mkdir $(dirname $FIFO)
mkfifo $FIFO

echo "Created"
du -a .

echo "Clean up"
DEST=./foo
find ./foo/bar -type f -exec mv {} "${DEST}" ';'
find ./foo/bar -depth -type d -exec rmdir -p {} + 2>/dev/null

echo "After"
du -a .

rm -fr ./foo

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衍生产品上运行。