我有一个文件夹,里面有很多文件夹,下面有很多文件夹等等。在这些最后的文件夹中是文件的小集群。我正在尝试将这些文件移动到主文件夹,并删除现在为空的文件夹层次结构。这就是我到目前为止所拥有的。
#!/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.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}
。
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的衍生版本上运行。