【问题标题】:How to remove question mark and the rest from file names?如何从文件名中删除问号和其余部分?
【发布时间】:2021-08-21 17:35:54
【问题描述】:

我已经使用 wget 抓取了整个网站模板。它在不同的子目录中创建了许多资产文件,其中文件名包含问号,例如./fonts/fontawesome-webfont.woff?v=4.5.0。我需要从这些文件名中截断 ? 和其余部分。我试过这个命令:

find . -type f -name '*\?*' -exec mv "{}" "$(echo {} | sed 's/\?.*//g')" \;

但它不起作用;没有任何东西被替换/删除,它显示了这个错误:

mv: './fonts/fontawesome-webfont.woff?v=4.5.0' and './fonts/fontawesome-webfont.woff?v=4.5.0' are the same file

我在不同的目录中有这些类型的文件名,所以我尝试了find 命令。我在这里错过了什么?

【问题讨论】:

    标签: bash sed find


    【解决方案1】:

    编辑

    Oguz ismail's solution 的变体:

    find . -name '*\?*' -type f -exec sh -c 'for fp; do echo mv -- "$fp" $(printf "%s" "$fp" | sed "s,?[^/]*$,,"); done' sh {} +
    ## or
    find . -name '*\?*' -type f -exec sh -c 'for fp; do echo mv "$fp" $(echo "$fp" | sed "s,?[^/]*$,,"); done' sh {} +
    

    find 命令从当前文件夹 (.) 开始搜索文件名 (-name '*\?*') 中包含 ? 字符的文件 (-type f),并在找到文件后搜索sh 脚本被执行。该脚本是for fp; do echo mv -- "$fp" $(printf "%s" "$fp" | sed "s,?[^/]*$,,"); done,它遍历找到的文件路径(fp),并移动(重命名)具有相同文件路径的文件路径,而不是文件路径中以第一个? 字符开头的部分。

    sed "s,?[^/]*$,," 命令实际上找到一个?,然后找到除/ 之外的任意数量的字符,直到字符串的末尾,然后从文件路径中删除这部分(因此,它只删除文件的一部分名称,文件夹名称可以包含? 字符)。

    mv 之后的 -- 是必要的,以防文件路径以 - 字符开头。

    注意:从上述命令中删除echo 以实际重命名文件。

    Linux 18.04 测试解决方案,perl-rename

    你可以使用

    find . -depth -type f -exec rename -n -v 's/\?.*//' {} +
    

    检查结果,如果结果符合预期,请删除 -n,这意味着“干”运行仅检查命令的工作情况。这里,-v(“verbose”选项)用于打印成功重命名的文件名。

    您可以阅读有关如何安装和使用此rename command here 的信息。

    【讨论】:

    • 请注意,有许多名为 rename 的不同工具。在某些 Linux 发行版上,这个名称为 perl-renameprename
    • 我运行了rename -vn 's/\?.*//' $(find .),它以状态 4 退出,没有打印,没有任何改变。
    • 4 表示:nothing was renamed (man page)
    • @Chitholian 试试find . -depth -type f -exec rename -n -v 's/\?.*//' {} +
    • rename --version 显示 rename from util-linux 2.36.2,在我的 Fedora-34 KDE 中,find . -depth -type f -exec rename -n -v 's/\?.*//' {} + 什么都不做,只是退出 1
    【解决方案2】:

    请考虑@Wiktor's answer,这个答案纯粹关注find 命令


    comments 中所述,sedfind 找到文件之前被调用。我们可以使用-exec bash -c 解决这个问题,并将路径名({})作为参数传递:

    find . -type f  -exec bash -c 'echo mv $0 $(sed "s/\?.*//g" <<< $0)' "{}" \;
    

    如果输出似乎为每个文件找到,请删除 echo 前缀到 mv 文件:

    find . -type f  -exec bash -c 'mv $0 $(sed "s/\?.*//g" <<< $0)' "{}" \;
    

    我本地机器上的小例子:

    $ ls -lt
    total 0
    -rw-r--r-- 1 me wheel 0 Jun  3 17:32 'something.exe?v=23456'
    -rw-r--r-- 1 me wheel 0 Jun  3 17:32 'myrandom_file?x=123'
    -rw-r--r-- 1 me wheel 0 Jun  3 17:32 'fontaweietsanderssome-webfont.woff?v=4.5.0'
    $
    $ find . -type f  -exec bash -c 'mv $0 $(sed "s/\?.*//g" <<< $0)' "{}" \;
    $
    $ ls -lt
    total 0
    -rw-r--r-- 1 me wheel 0 Jun  3 17:32 something.exe
    -rw-r--r-- 1 me wheel 0 Jun  3 17:32 myrandom_file
    -rw-r--r-- 1 me wheel 0 Jun  3 17:32 fontaweietsanderssome-webfont.woff
    $
    

    【讨论】:

      【解决方案3】:

      假设 rename 不可用(或者您无法安装它),我可能会选择更直接且更容易 (?) 理解/维护的东西。

      假设我在名为question 的目录下有以下内容:

      $ ls -1 question
      abc?v=4.5.0
      def.txt
      ghi.pdf?v=1.4.3
      

      使用简单的while 循环和parameter substitution 的一个想法:

      find . -type f -name '*\?*' |
      while read -r fname
      do
          echo mv "${fname}" "${fname//\?*/}"
      done
      

      这会生成:

      mv ./question/abc?v=4.5.0 ./question/abc
      mv ./question/ghi.pdf?v=1.4.3 ./question/ghi.pdf
      

      一旦您对生成的mv 命令感到满意,就可以删除echo 以便实际运行mv 命令。

      【讨论】:

        【解决方案4】:

        这是一个防弹的。

        find . -name '*\?*' -type f -exec sh -c '
        for fp; do
          fn=${fp##*/}
          echo mv "$fp" "${fp%/*}/${fn%%\?*}"
        done' sh {} +
        

        上面的循环隔离了每个选定文件的名称(在名为fn 的变量中),以便能够删除最左边的问号和其余的问号而不会破坏任何内容;如果我们改为使用${fp%%\?*},我们将无法处理名称中带有问号的目录的内容。虽然如果我们确定每个文件名只包含一个问号,${fp%\?*} 就可以正常工作。

        如果输出看起来不错,请删除 echo

        【讨论】:

          猜你喜欢
          • 2018-03-19
          • 2013-12-10
          • 1970-01-01
          • 2021-03-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-04-08
          相关资源
          最近更新 更多