是否销毁目录的层次结构

  • 本文关键字:层次结构 是否 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/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}

rmdir -p命令删除空的目录(想想-p中的"prune"),工作深度优先。

GNUmv有一个非常有用的选项-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命令(而对于GNUmv,可以通过一次调用移动许多文件)。否则,它是等价的。

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

请注意,如果$DEST与您正在搜索的目录相同,则在将$DEST中已经存在的文件移动到其自身上时会遇到问题。正如所写的,代码并不能避免这个问题。如有必要,您可以使用来避免这种情况

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

尾部斜杠强制执行"onlydirectories"(将其视为等效于"$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的衍生版本上运行。