【问题标题】:bash rename spaces - also do sub files and folders as wellbash 重命名空间 - 也可以做子文件和文件夹
【发布时间】:2012-07-22 05:58:20
【问题描述】:

我有这个脚本,它只执行父文件夹,它不会重命名子文件夹和文件——我希望它可以去掉所有带有 _ 的非数字。

#!/bin/bash
for f in *
do
   new="${f// /_}"
   if [ "$new" != "$f" ]
   then
      if [ -e "$new" ]
      then
         echo not renaming \""$f"\" because \""$new"\" already exists
      else
         echo moving "$f" to "$new"
         mv "$f" "$new"
      fi
   fi
done

【问题讨论】:

    标签: linux bash scripting rename numeric


    【解决方案1】:

    编辑:对不起,我忘记了空格。我的标准处理方式是读取循环(假设文件名称中不能有换行符)

    find | {
    read f
    while [ -n "$f" ]
    do
        # process "$f" (always with the quotes)
        read f
    done
    }
    

    【讨论】:

    • 这不起作用,文件名中的空格使它们成为单独的“文件”。因此,如果您有一个带有空格的目录“qwe asd”,则 f 将是“qwe”,然后是“asd”。
    • @JonLin 没错,正在等待解决这个问题的解决方案。
    【解决方案2】:

    在最近的 bash 版本中,你可以使用

    for f in **
    

    您可能需要设置 globstar 选项才能使其工作:

    shopt -s globstar
    

    【讨论】:

    • 我该把 shopt -s globstar 放在哪里?
    • 这是我得到的:sh re.sh re.sh: line 2: shopt: globstar: invalid shell option name
    • @thevoipman:不要使用sh 调用bash 脚本,使用bashsh 会打开一个特殊的sh 兼容模式,该模式会禁用许多功能。或者,您的 bash 可能太旧而无法支持该功能。
    • 你好,刚刚这样做了,它是这样说的:bash re.sh re.sh: line 2: shopt: globstar: invalid shell option name
    • 这很奇怪,我运行的是 centos 6.2,我认为一切都是最新最好的
    【解决方案3】:

    您也可以尝试递归运行命令(可能有一种方法可以在不调用相同脚本的情况下执行此操作,但我现在无法让它工作):

    假设此脚本在您的路径中或此处/usr/local/bin/remove_spaces.sh

    #!/bin/bash 
    
    for f in *
    do
       if [ -d "$f" ]
       then
          cd "$f"
          /usr/local/bin/remove_spaces.sh
          cd ..
       fi
    
       new="${f// /_}"
       if [ "$new" != "$f" ]
       then
          if [ -e "$new" ]
          then
         echo not renaming \""$f"\" because \""$new"\" already exists
          else
         echo moving "$f" to "$new"
         mv "$f" "$new"
          fi
       fi
    done
    

    所以你基本上要检查当前文件f 是否是一个目录,如果是,cd 到它并重新运行命令,然后 cd 退出并在需要时进行重命名。

    【讨论】:

    • 哇,这比我想象的要复杂得多。我以为我可以做类似 mv -R * abc+1.* 的事情然后完成:) b
    • @thevoipman 我只添加了第一个 if 块,其他一切都与您的脚本相同。
    【解决方案4】:

    如果文件名不能包含制表符或换行符(注意引号):

    IFS="$(printf '\n\t')"
    for file in $(find .) ; do
      COMMAND "$file" ...
    done
    

    另见Filenames and Pathnames in Shell

    【讨论】:

      【解决方案5】:

      获取文件和目录列表:

      要递归地操作文件,与 glob 相比,使用 find 是更好的解决方案。我建议在开始操作之前使用文件名填充 bash 数组。此外,我认为一次执行此步骤,目录然后文件,将是谨慎的。您不想重命名目录,然后在重命名文件时发现该文件不存在。出于同样的原因,该脚本在文件系统层次结构的更深层次上工作也很重要(因此在下面使用sort)。

      对列表进行操作:

      获得列表后,您可以调用一个通用的 shell 函数来进行文件或目录名称的规范化和重命名。请注意正确引用名称以获得所需内容的重要性。这一点非常重要,因为 bash(或任何 shell)在解析命令行时使用空格作为单词边界。

      脚本:

      以下脚本(在下面的示例输出中名为 ./rename_spaces.bash)应该可以执行您想要的操作。要添加您自己的怪异字符,请将它们添加到 weirdchars 变量中。请注意,您需要适当地转义字符(例如单引号已被转义)。如果新文件名存在,脚本会跳过一条消息。这也意味着它将打印用于琐碎重命名的消息(原始名称中没有奇怪字符的文件名)。这对某些人来说可能很烦人(例如我:-p)

      #!/bin/bash
      
      # set -o xtrace # uncomment for debugging
      
      declare weirdchars=" &\'"
      
      function normalise_and_rename() {
        declare -a list=("${!1}")
            for fileordir in "${list[@]}";
            do
                newname="${fileordir//[${weirdchars}]/_}"
                [[ ! -a "$newname" ]] && \
                  mv "$fileordir" "$newname" || \
                      echo "Skipping existing file, $newname."
            done
      }
      
      declare -a dirs files
      
      while IFS= read -r -d '' dir; do
          dirs+=("$dir")
      done < <(find -type d -print0 | sort -z)
      
      normalise_and_rename dirs[@]
      
      while IFS= read -r -d '' file; do
          files+=("$file")
      done < <(find -type f -print0 | sort -z)
      
      normalise_and_rename files[@]
      

      这是一个示例输出,其中包含目录树,其中目录和文件在运行上述脚本之前和之后名称中有空格。

      $ tree
       .
       ├── dir1
       │   ├── subdir1\ with\ spaces
       │   │   └── file1
       │   └── subdir2\ with\ spaces&weird\ chars'
       │       └── file2
       ├── dir2
       │   ├── subdir1\ with\ spaces
       │   │   └── file1\ with\ space
       │   └── subdir2\ with\ spaces
       │       └── file2\ with\ space
       ├── dir3
       │   ├── subdir1
       │   │   └── file1
       │   ├── subdir2
       │   │   └── file2
       │   └── subdir3
       │       └── file3
       └── rename_spaces.bash
      
       10 directories, 8 files
       $ ./rename_spaces.bash
       $ tree
       .
       ├── dir1
       │   ├── subdir1_with_spaces
       │   │   └── file1
       │   └── subdir2_with_spaces_weird_chars_
       │       └── file2
       ├── dir2
       │   ├── subdir1_with_spaces
       │   │   └── file1_with_space
       │   └── subdir2_with_spaces
       │       └── file2_with_space
       ├── dir3
       │   ├── subdir1
       │   │   └── file1
       │   ├── subdir2
       │   │   └── file2
       │   └── subdir3
       │       └── file3
       └── rename_spaces.bash
      
       10 directories, 8 files
      

      注意: 实现脚本以对任何非字母数字的内容“做正确的事”似乎并非易事。例如,我不确定如何处理文件或目录名称中的点或预先存在的下划线或其他“常规”允许的字符。

      以通用方式识别不良/特殊字符也是一个问题。在国际语言环境中,情况更加复杂。我不知道任何简单的说法允许“只允许英文字母中的数字或字符”。如果有人有想法,请继续并发布答案。

      【讨论】:

      • 您好,到目前为止,这对我来说似乎是完美的解决方案。问你是否也可以用_替换任何不是字母数字的东西是不是太过分了?我在文件夹和文件中有一些重音符号和符号。再次感谢。
      • @thevoipman 看看我的编辑。我在脚本中添加了对任意特殊字符的支持,并在文本中添加了关于如何配置它的注释。玩得开心。 :)
      • 我重新阅读了您的评论,并尝试更改脚本以对任何非字母数字内容执行正确的操作。但这似乎不是微不足道的。阅读答案末尾的我的笔记。
      • +1 非常好的答案,非常注重细节。唯一的小问题是,如果已经存在具有相同目标名称的另一个文件(例如,“odd'name”和“odd_name”),它会默默地重命名文件失败。将此类文件名打印到 stderr 并中止可能就足够了,因此用户可以手动解决冲突。
      • +10 for @suvayu - 我确实理解您所说的关于点和 _ 的内容 - 也许我们可以保留/忽略它们,因为我们将其用作占位符。我将测试新脚本并回复您。
      猜你喜欢
      • 2011-12-30
      • 2019-05-11
      • 2020-08-29
      • 2016-01-25
      • 1970-01-01
      • 1970-01-01
      • 2015-02-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多