bash重命名空格 - 也可以执行子文件和文件夹

时间:2012-07-23 23:30:07

标签: linux bash scripting rename numeric

我有这个脚本只做父文件夹,它没有重命名子文件夹和文件 - 我希望得到它来剥离所有非数字与_。

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

5 个答案:

答案 0 :(得分:4)

获取文件和目录列表:

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

在列表上操作:

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

脚本:

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

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

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

答案 1 :(得分: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退出并在需要时重命名。

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

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

for f in **

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

shopt -s globstar

答案 4 :(得分:0)

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

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

另见Filenames and Pathnames in Shell