UNIX Shell脚本:如何将文件递归移动到一个目录中?


8

我有大量的小文件f排列在目录结构中,如下所示:

/A/B/C/f

A级别有11个目录,B级别每个目录有约100个目录,C级别每个目录有约30个目录,每个目录都有一个文件f。

如何将所有文件上移一级?例如,给定这组文件...

/ A / B / C / f1
/ A / B / C / f2
/ A / B / C / f3
/ A / B / C / f4

我希望目录/A/B/包含4个文件,从f1到f4。不需要删除目录C。

我希望这是一个解决问题,可能涉及findxargswhatnot。有任何想法吗?

干杯,

詹姆士

Answers:


9

使用GNU find(在Linux上可以找到)或任何其他支持-execdir以下功能的发现相当简单:

find A -type f -execdir mv -i {} .. \;

符合标准find

find A -type f -exec sh -c 'mv -i "$1" "${1%/*}/.."' sh {} \;

使用zsh:

zmv -Q -o-i 'A/(**/)*/(*)(.)' 'A/$1$2'

如果目录结构始终具有相同的嵌套级别,则不需要任何递归遍历(但首先删除空目录):

for x in */*; do; echo mv -i "$x"/*/* "$x"/..; done

1

对于那组文件,这样做:

$ cd /A/B/C/
$ mv ./* ../

但是我希望您的问题更加复杂...我无法回答...我不确定您的目录结构如何...您能澄清一下吗?


这显然适用于任何一个目录/ A / B / C,但是由于我有大约30000个这样的目录,我希望可以在层次结构顶部执行的命令一次处理所有这些目录。
jl6

1
@ jl6我知道了,只有文件需要移动?因此,C不必移动到B,B不必移动到A,等等...
堆栈溢出已死

没错-该操作仅应在层次结构最低级别的文件上执行,并且只能将其上移一层。
2010年

0

我的第一个猜测是

$ find A -type f -exec mv {} .. \;  

只要您不指定-depth它就可以。我还没有尝试过,因此请先进行测试,然后再提交。


有两个名称相同的文件时,发现有什么作用?

查找不是问题,嵌入的“ mv”命令是问题。由于mv不会重命名,因此将被覆盖,因此重复名称不理想。您可以使用mv -i有所帮助,但是拥有30,000+个文件可能会很痛苦。如果您需要对情况进行控制,则可能需要使用脚本语言编写的程序(我的选择是python,YMMV)。
hotei 2010年

我没有在mv中使用“备份”,但是在手册页上看,这似乎可以解决您的重复名称问题。“找到编号为{} .. \;的A型f -exec mv --backup” 可能有效(请注意,这是--backup中的双破折号)
hotei 2010年

给定的find示例会将最低级别的所有文件移动到A上方的目录(我相信“ ..”在传递给find / mv?之前会被外壳程序解释)。我需要文件在层次结构中仅上移一层。进一步说明:只要文件仅上移一级,就不会有重复的文件名。
jl6

@hotei:这些mv命令在当前目录中执行,因此..不会执行您希望的操作。请参阅我的答案以获取解决方案。@ jl6:shell与无关..,由内核处理。
吉尔(Gilles)“所以,别再邪恶了”,2010年

0

如果您只想移动叶目录中的文件(即如果包含子目录/A/B/file/A则不想移动到该B目录),则可以通过以下两种方法:

两者都需要这个

leaf ()
{
    find $1 -depth -type d | sed 'h; :b; $b; N; /^\(.*\)\/.*\n\1$/ { g; bb }; $ {x; b}; P; D'
}
shopt -s nullglob

这个作品:

leaf A | while read -r dir
do
    for file in "$dir"/*
    do
        parent=${dir%/*}
        if [[ -e "$parent/${file##*/}" ]]
        then
            echo "not moved: $file"
        else
            mv "$file" "$parent"
        fi
    done
done

这样会更快,但是它不喜欢空的源目录:

leaf A | while read -r dir
do
    mv -n "${dir}"/* "${dir%/*}"
    remains=$(echo "$dir"/*)
    [[ -n "$remains" ]] && echo "not moved: $remains"
done
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.