【问题标题】:File to Folder .bat to .sh文件到文件夹 .bat 到 .sh
【发布时间】:2018-05-20 03:59:08
【问题描述】:

我有一个旧的 .bat 文件,需要在我的 QNAP 上运行。我需要为每个文件递归创建文件夹(基于文件名),然后将文件移动到该文件夹​​。

这是旧的 .bat:

@echo off 
for %%a in (*.*) do (md "%%~na" 2>nul
move "%%a" "%%~na"
)

【问题讨论】:

    标签: shell file mkdir qnap


    【解决方案1】:

    如果我正确理解 .bat 语法,这应该基本上是等效的:

    #!/bin/sh
    for a in *
    do
        [ ! -f "$a" ] && continue  # skip anything that is not a regular file
        dirname="${a%.*}"          # trim off the last suffix
        mkdir -p "$dirname" && mv -i "$a" "$dirname/"
    done
    

    和原来的一样,这个脚本只会处理当前目录中的文件。如果您需要处理当前目录及其所有预先存在的子目录(这就是我对“递归”的理解)它会变得更加复杂,因为脚本必须以深度优先的方式进行以避免尝试将文件推送到无限深的 sub-sub-sub... 目录中。

    在这种情况下,需要开发不同的解决方案,很可能使用find 命令来收集文件名。 find 命令默认是递归的:它将自动处理指定的目录及其所有子目录,除非你明确告诉它不要。所以这里有一个递归版本,可以处理任意目录中任意数量的文件:

    #!/bin/sh
    # FIXME: needs more protection against unusual filename characters
    find . -type f -depth | while read a
    do
        dirname="${a%.*}"          # trim off the last suffix
        mkdir -p "$dirname" && mv -i "$a" "$dirname/"
    done
    

    不过,它有一个小问题:如果文件名包含换行符,它可能无法正确处理该文件名。但是,如果您的文件的名称中只有表现良好的字符,那么这就足够了。

    【讨论】:

    • shell 在for a in * 行的参数数量上没有问题,因为它没有在那里调用任何外部实用程序。
    • 为了文件名的安全:find . -type f -depth -exec sh -c 'for a do dirname="${a%.*}"; mkdir -p "$dirname" && mv -i "$a" "$dirname/"; done' sh {} +
    • 参数的数量不是问题:目录中文件名的总组合长度可能是。在现代常规系统上,它需要在特殊情况下超过 ARG_MAX,但请注意,我们在这里处理的是 QNAP(实际上是嵌入式系统)。在有大量文件的目录中,通配符扩展可能会达到最大命令行长度限制。
    • 只有当 shell 通过execve() 调用外部实用程序时,命令长度限制才会成为问题,这里的* 不会这样做。如果您想完全正确,您应该说“命令行和当前环境的组合长度”,因为环境变量也计入该限制。但在这种情况下这不是问题,因为与外部实用程序一起使用的命令行会很短。
    • 感谢您的澄清:过去,我在busybox或类似的嵌入式环境中被类似的东西所困扰,让该死的东西工作比了解整个问题更重要,所以看起来我有一些不必要的妄想症......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-22
    • 1970-01-01
    • 1970-01-01
    • 2015-05-17
    相关资源
    最近更新 更多