在terminal/bash脚本中将文件夹拆分为多个子文件夹

Bri*_*n C 9 directory bash mv filestructure

我有几个文件夹,每个文件夹有15,000到40,000张照片.我希望将每个文件拆分为子文件夹 - 每个文件夹中包含2,000个文件.

有什么快速的方法可以创建我需要的每个文件夹并移动所有文件?

目前我只能找到如何将文件夹中的前x个项目移动到预先存在的目录中.为了在包含20,000个项目的文件夹上使用它...我需要手动创建10个文件夹,并运行命令10次.

ls -1  |  sort -n | head -2000| xargs -i mv "{}" /folder/
Run Code Online (Sandbox Code Playgroud)

我尝试将它放入for循环中,但是在使用mkdir正确制作文件夹时遇到了麻烦.即使我解决了这个问题,我还是需要程序只为每个第20个文件创建文件夹(新组的开始).它想为每个文件创建一个新文件夹.

那么......我怎样才能轻松地将大量文件移动到每个文件中任意数量文件的文件夹中?

任何帮助都会......非常......乐于助人!

Gio*_*ssi 13

这个解决方案在 MacOS 上对我有用:

i=0; for f in *; do d=dir_$(printf %03d $((i/100+1))); mkdir -p $d; mv "$f" $d; let i++; done
Run Code Online (Sandbox Code Playgroud)

它创建每个包含 100 个元素的子文件夹。


tmp*_*tmp 11

尝试这样的事情:

for i in `seq 1 20`; do mkdir -p "folder$i"; find . -type f -maxdepth 1 | head -n 2000 | xargs -i mv "{}" "folder$i"; done
Run Code Online (Sandbox Code Playgroud)

完整脚本版本:

#!/bin/bash

dir_size=2000
dir_name="folder"
n=$((`find . -maxdepth 1 -type f | wc -l`/$dir_size+1))
for i in `seq 1 $n`;
do
    mkdir -p "$dir_name$i";
    find . -maxdepth 1 -type f | head -n $dir_size | xargs -i mv "{}" "$dir_name$i"
done
Run Code Online (Sandbox Code Playgroud)

  • 对于 Mac 操作系统,需要对 xargs `find 进行调整。-最大深度 1 -类型 f | head -n $dir_size |xargs -J {} mv {} "$dir_name$i"` (3认同)

Mic*_*ros 5

此解决方案可以处理带有空格和通配符的名称,并且可以轻松扩展以支持不太直接的树结构。它将在工作目录的所有直接子目录中查找文件,并将它们分类到这些目录的新子目录中。新目录将被命名为01等:

#!/bin/bash

maxfilesperdir=20

# loop through all top level directories:
while IFS= read -r -d $'\0' topleveldir
do
        # enter top level subdirectory:
        cd "$topleveldir"

        declare -i filecount=0 # number of moved files per dir
        declare -i dircount=0  # number of subdirs created per top level dir

        # loop through all files in that directory and below
        while IFS= read -r -d $'\0' filename
        do
                # whenever file counter is 0, make a new dir:
                if [ "$filecount" -eq 0 ]
                then
                        mkdir "$dircount"
                fi

                # move the file into the current dir:
                mv "$filename" "${dircount}/"
                filecount+=1

                # whenever our file counter reaches its maximum, reset it, and
                # increase dir counter:
                if [ "$filecount" -ge "$maxfilesperdir" ]
                then
                        dircount+=1
                        filecount=0
                fi
        done < <(find -type f -print0)

        # go back to top level:
        cd ..
done < <(find -mindepth 1 -maxdepth 1 -type d -print0)
Run Code Online (Sandbox Code Playgroud)

与进程替换的find -print0/read组合已从另一个问题中窃取。

应该注意的是,简单的通配符也可以处理各种奇怪的目录和文件名。然而,对于多个级别的目录,它并不容易扩展。


nis*_*ama 5

下面的代码假定文件名不包含换行符,空格,制表符,单引号,双引号或反斜杠,并且文件名不以破折号开头。它还假定IFS尚未更改,因为它使用while read代替while IFS= read,并且因为未引用变量。添加setopt shwordsplitZsh。

i=1;while read l;do mkdir $i;mv $l $((i++));done< <(ls|xargs -n2000)
Run Code Online (Sandbox Code Playgroud)

下面的代码假定文件名不包含换行符,并且它们不以短划线开头。-n2000一次接受2000个参数,它{#}是作业的序号。替换{#}'{=$_=sprintf("%04d",$job->seq())=}'以四位数填充数字。

ls|parallel -n2000 mkdir {#}\;mv {} {#}
Run Code Online (Sandbox Code Playgroud)

下面的命令假定文件名不包含换行符。它使用renameAristotle Pagaltzis 的实现,这是renameHomebrew中的公式,在哪里-p需要创建目录,在哪里--stdin需要从STDIN获取路径,在哪里$N是文件编号。在其他实现中,可以使用$.++$::i代替$N

ls|rename --stdin -p 's,^,1+int(($N-1)/2000)."/",e'
Run Code Online (Sandbox Code Playgroud)

  • 使用`ls`会带来很多问题。我对您的示例做了一些修改:`find。-类型f -print0 | parallel -0 -n2000 mkdir“ dir _ {#}” \; mv {}“ dir _ {#}”`。对我来说同样有效。 (2认同)