【问题标题】:Script to group numbered files into folders将编号文件分组到文件夹中的脚本
【发布时间】:2014-12-30 10:01:09
【问题描述】:

我在一个文件夹中有大约一百万个文件,格式为 xxxx_description.jpg,其中 xxx 是一个从 100 到未知上限的数字。

列表与此类似:

146467_description1.jpg 146467_description2.jpg 146467_description3.jpg 146467_description4.jpg 14646_description1.jpg 14646_description2.jpg 14646_description3.jpg 146472_description1.jpg 146472_description2.jpg 146472_description3.jpg 146500_description1.jpg 146500_description2.jpg 146500_description3.jpg 146500_description4.jpg 146500_description5.jpg 146500_description6.jpg

为了在 at 文件夹中获取文件编号,我想将它们全部放入按开头编号分组的文件夹中。

即: 146467/146467_description1.jpg 146467/146467_description2.jpg 146467/146467_description3.jpg 146467/146467_description4.jpg 14646/14646_description1.jpg 14646/14646_description2.jpg 14646/14646_description3.jpg 146472/146472_description1.jpg 146472/146472_description2.jpg 146472/146472_description3.jpg 146500/146500_description1.jpg 146500/146500_description2.jpg 146500/146500_description3.jpg 146500/146500_description4.jpg 146500/146500_description5.jpg 146500/146500_description6.jpg

我正在考虑尝试使用命令行:find | awk {} | mv 命令或者写一个脚本,但我不确定如何最有效地做到这一点。

【问题讨论】:

    标签: file shell unix scripting directory


    【解决方案1】:

    如果你真的要处理数百万个文件,我怀疑 glob (*.jpg[0-9]*_*.jpg 可能会失败,因为它会生成一个对于 shell 来说太长的命令行。如果是这样,你仍然可以使用find. 这样的事情可能会奏效:

    find /path -name "[0-9]*_*.jpg" -exec sh -c 'f="{}"; mkdir -p "/target/${f%_*}"; mv "$f" "/target/${f%_*}/"' \;
    

    为了便于阅读,我们正在这样做:

    • find /path - 运行查找,以/path 为起点,
    • -name "[0-9]*_*.jpg" - 匹配所有目录中与此文件规范匹配的文件,
    • -exec sh -c 在每个文件上执行以下...
      • 'f="{}"; - 将文件名放入变量中...
      • mkdir -p "/target/${f%_*}"; - 根据该变量创建一个目标目录(阅读 mkdir 的有关 -p 选项的手册页)
      • mv "$f" "/target/${f%_*}/"' - 将文件移动到目录中。
      • \; - 结束 -exec 表达式

    从好的方面来说,它可以处理find 可以处理的任意数量的文件(即仅受您的操作系统限制)。不利的一面是,它为每个要处理的文件启动了一个单独的 shell。

    请注意,以上答案适用于 Bourne/POSIX/Bash。如果您使用 CSH 或 TCSH 作为您的 shell,以下可能会起作用:

    #!/bin/tcsh
    
    foreach f (*_*.jpg)
      set split = ($f:as/_/ /)
      mkdir -p "$split[1]"
      mv "$f" "$split[1]/"
    end
    

    这假定文件规范将适合 tcsh 的 glob 缓冲区。我在一个命令行上测试了 40000 个文件 (894KB),在 FreeBSD 中使用 /bin/sh 或 /bin/csh 没有问题。 就像上面的 Bourne/POSIX/Bash 参数扩展解决方案一样,这避免了对外部的不必要调用,我还没有测试过,并且会推荐 find 解决方案,即使它更慢。

    【讨论】:

    • 这就是我自己尝试做的事情。是的,我确实在一个文件夹中处理超过一百万个文件。 :( 几年后有些决定很糟糕....
    【解决方案2】:

    你可以使用这个脚本:

    for i in [0-9]*_*.jpg; do
       p=`echo "$i" | sed 's/^\([0-9]*\)_.*/\1/'`
       mkdir -p "$p"
       mv "$i" "$p"
    done
    

    【讨论】:

    • 我希望如果有数百万个文件,那么这个 glob 的扩展对于 shell 来说会太大。你知道行长限制吗?
    • 那似乎是个问题。 :)
    • 此外,即使对于 OP 的 shell(未指定)来说这不是太长,您也会为每个文件启动三个进程。为此使用sed 是一种浪费。
    • 不,正如您所指出的,OP 将他的问题标记为“shell”,而不是 CSH、Bash、POSIX、Bourne、KSH 或任何其他特定的 shell。您的答案适用于一个特定的外壳(或它们的家族),但您没有提到哪个外壳。 (是的,这对我们这些知道的人来说是显而易见的,但具体而不是做出假设不是更好吗?)
    • 我们最好专注于重要的事情,而不是浪费时间在这个无用的论点上:) 我将从这个答案中删除我的 cmets。
    【解决方案3】:

    使用grep

       for file in *.jpg; 
        do 
        dirName=$(echo $file | grep -oE '^[0-9]+')
        [[ -d $dirName ]] || mkdir $dirName
        mv $file $dirName
        done
    

    grep -oE '^[0-9]+' 将文件名中的起始数字提取为

    146467
    146467
    146467
    146467
    14646
    ...
    

    [[ -d $dirName ]] 如果目录存在则返回1

    [[ -d $dirName ]] || mkdir $dirName 确保 mkdir 仅在测试 [[ -d $dirName ]] 失败时才有效,即目录不存在

    【讨论】:

    • 虽然它有效,但这个答案会产生很多“目录存在”消息。
    • 我已经更正了答案。希望它现在可以正常工作
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-28
    • 1970-01-01
    相关资源
    最近更新 更多