【问题标题】:Bash rename zips in subdirectoriesBash 重命名子目录中的 zip
【发布时间】:2012-02-12 23:58:12
【问题描述】:

你好,有一堆文件夹,里面有一个 zip 文件,路径如下所示

/folder/subfolder/archive.zip
/folder/subfolder1/archive1.zip
/folder/subfolder2/archive2.zip
/folder/subfolder3/zip3.zip
/folder/subfolder4/zip4.zip
etc...

我需要这样的输出

/folder/subfolder/preview.zip
/folder/subfolder1/preview.zip
/folder/subfolder2/preview.zip
/folder/subfolder3/preview.zip
/folder/subfolder4/preview.zip
etc...

每个文件夹也只有一个 zip,所以我不必进行覆盖工作,而且我只需要将每个文件夹中的每个 zip 重命名为 preview.zip,无论每个子目录中的名称如何,我该怎么做在 bash 中?

感谢您的帮助

【问题讨论】:

    标签: bash rename loops mv


    【解决方案1】:

    这样的事情应该可以完成:

    for j in **/*.zip; do mv "$j" "${j%/*.zip}/preview.zip"; done
    

    正如 SiegeX 所指出的,请注意,要使用递归通配符,您必须设置 globstar 选项,该选项仅在 bash 4.x 中可用

    【讨论】:

    • 别忘了提到这在许多实现上都不起作用,因为它是 Bash 4.X 独有的功能,而且你也没有说任何关于启用 globstar 的内容
    • 如何处理文件名中的空格?
    • @Johnsyweb 很糟糕,因为他没有引用他的变量
    • @SiegeX:一个反问;)
    • @Johnsyweb 感谢您的评论。我已经添加了缺少的引号,现在该案例将得到处理。
    【解决方案2】:

    您可以通过 Bash 的参数替换来做到这一点:

    bash-3.2$ for archive in folder/subfolder*/*.zip; do
        echo "Archive = ${archive}, Preview = ${archive%/*}/preview.zip"
    done
    Archive = folder/subfolder/archive.zip, Preview = folder/subfolder/preview.zip
    Archive = folder/subfolder1/archive1.zip, Preview = folder/subfolder1/preview.zip
    Archive = folder/subfolder2/archive2.zip, Preview = folder/subfolder2/preview.zip
    Archive = folder/subfolder3/zip3.zip, Preview = folder/subfolder3/preview.zip
    Archive = folder/subfolder4/zip4.zip, Preview = folder/subfolder4/preview.zip
    

    ${archive%/*} 将剥离 ${archive} 中最后一个 / 的所有内容。

    这将允许您验证将运行的命令。将其更改为:

    mv "${archive}" "${archive%/*}/preview.zip" 
    

    ...重命名文件(即使它们的名称中有空格)。

    【讨论】:

    • 他不希望对输出文件进行编号。就preview.zip
    • @SiegeX:我认为这个答案不值得反对。它使用GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin11) 进行了测试,没有使用~/.bashrc
    • 好的,我看到了问题所在。您确实没有使用 bash 4.X 功能(即 globstar),但在低于 Bash 4.X 的任何东西上,folder/**/*.zipfolder/*/*.zip 也没有区别。额外的* 是多余的。如果您编辑,我将删除反对票。
    • @SiegeX 看来你今晚没有心情在bash 中接受不好的答案。 :)
    • +1。我喜欢这篇文章中的所有答案。每个答案都提供了很多值得学习的东西。 @SiegeX 如果您不介意,请在您的回答中添加您分享的有关 globstar 的信息,我相信每个人都会欣赏这些信息并且值得投票。至少马上给我一份。
    【解决方案3】:

    这可能对您有用,但尚未经过全面测试:

    find /folder -maxdepth 1 -mindepth 1 -type d -exec mv {}/archive*.zip {}/preview.zip \;
    

    在您的示例下非常严格地工作,这 应该 find /folder 下的所有目录,最大和最小深度为 1(即仅在 /folder 内的目录,因为这似乎成为你想要的)并为每个文件下的文件执行mv 命令。

    【讨论】:

    • 这行不通。它将尝试将archive*.zip 文件移动到名为<something>/preview.zip 的目录中
    • @SiegeX,它似乎对我有用,通过基本测试,减去*。看起来 find 不想将它作为文字 * 以外的任何东西传递。
    • @Johnsyweb 如果您尝试它,它将不起作用,至少对于同一目录下以archive开头的多个文件不起作用。正如 Dan 指出的那样,它不像 * 所写的那样工作,但即使 shell did 在成为 * 之前扩展 -exec 它与发出以下内容没有什么不同命令:mv /foo/archive1.zip /foo/archive2.zip /foo/archive3.zip foo/preview.zip。由于mv 使用最后一个参数作为它的目的地,它会尝试完全按照我在第一条评论中所说的去做,即将三个文件移动到一个名为/foo/archive.zip的目录中
    • @SiegeX,对于它的价值,OP 明确表示“每个文件夹也只有一个 zip,所以我不必处理覆盖等问题”,所以这部分不是这个具体案例的问题。但星号是。
    【解决方案4】:
    #!/bin/bash
    while read file; do
      echo mv "$file" "${file%/*}/preview.zip"
    done < <(find /folder -type f -name "archive*.zip")
    

    注意:如果输出看起来足够,请删除echo,然后重新运行脚本以进行实际更改

    【讨论】:

    • 在这种情况下我们是否需要-print0 来处理带有空格的文件名?还是无论如何都会起作用?
    • @JaypalSingh 是的。要完全不受名称中带有空格的文件的影响,甚至是带有其他讨厌的文件(如换行符)的文件,最好的方法是将-print0 添加到find 的末尾并使用while IFS= read -r -d '' file; do ... 我对此有点懒惰使用这个,因为他想要定位的文件名都是已知的并且格式正确。最好的解决方案带有 globstar 的 Bash 4.X 版本,因为您不必担心所有这些东西;但是...由于 Bash 3.X 拥有更大的安装基数,因此您严重限制了您的受众
    猜你喜欢
    • 2021-11-27
    • 2016-08-03
    • 2019-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-30
    • 2015-08-02
    • 1970-01-01
    相关资源
    最近更新 更多