1

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

#!/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
4

5 回答 5

4

获取文件和目录列表:

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

在列表上操作:

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

剧本:

以下脚本(./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

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

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

于 2012-07-24T07:40:42.380 回答
1

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

假设此脚本在您的路径中或此处/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 退出并在需要时进行重命名。

于 2012-07-23T23:54:46.463 回答
0

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

find | {
read f
while [ -n "$f" ]
do
    # process "$f" (always with the quotes)
    read f
done
}
于 2012-07-23T23:33:25.170 回答
0

在最新版本的 bash 中,您可以使用

for f in **

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

shopt -s globstar
于 2012-07-23T23:37:23.720 回答
0

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

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

另请参见Shell 中的文件名和路径名

于 2012-07-24T06:42:01.157 回答