2012-07-23 45 views
1

我有这个脚本,只有父文件夹,它不重命名子文件夹和文件 - 我希望得到它剥离所有非数字与_到它。bash重命名空间 - 也做子文件和文件夹以及

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

获取文件和目录列表:

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

操作上列出:

一旦你的列表,你可以调用一个共同的外壳函数来完成文件或目录名正火和重命名。请注意正确引用名称以获得您想要的内容的重要性。这是非常重要的,因为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 

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

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

+0

您好,这对我来说似乎是完美的解决方案。问你是否也可以用_代替任何非字母数字的东西太多了?我有一些口音,文件夹和文件中的符号。 再次感谢。 – thevoipman 2012-07-24 14:54:39

+0

@thevoipman看看我的编辑。我向脚本添加了对任意特殊字符的支持,并添加了关于如何在文本中配置它的注释。玩的开心。 :) – suvayu 2012-07-24 18:27:57

+0

我重读你的评论,并试图改变脚本做正确的事情,非字母数字。但它似乎是不平凡的。在答案结尾阅读我的笔记。 – suvayu 2012-07-24 18:51:10

0

编辑:对不起,我忘记了空格。我的操作标准的方式,是一个读循环(假定文件中不能有名字换行)

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

这不起作用,文件名中的空格使它们分开“文件”。所以如果你有一个空格为“qwe asd”的目录,f会是“qwe”,然后是“asd”。 – 2012-07-23 23:36:04

+0

@JonLin这是正确的,我正在等待一个解决方案来克服这个问题。 – thevoipman 2012-07-23 23:45:16

+0

好点。我忘记了空白。添加了它 – Sodved 2012-07-24 13:44:33

0

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

for f in ** 

您可能需要设置globstar为了选择,使其工作:

shopt -s globstar 
+0

我在哪里可以购买那个shopt -s globstar? – thevoipman 2012-07-23 23:44:33

+0

@thevoipman:在第一次使用**之前的任何地方。 – choroba 2012-07-23 23:45:30

+0

下面是我得到的: sh re.sh re.sh:line 2:shop:globstar:无效shell选项名称 – thevoipman 2012-07-23 23:53:14

1

您也可以尝试递归运行命令(有可能是一个办法做到这一点,而无需调用相同的脚本,但我不能得到它的工作里GHT现在):

说这个脚本是在你的路径,或在这里/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退出并根据需要进行重命名。

+0

哇,这会变得更复杂,然后我想。我以为我可以做一些像mv -R * abc + 1. *然后完成:) b – thevoipman 2012-07-24 00:04:58

+0

@thevoipman我只添加了第一个if块,其他所有内容与您的脚本相同。 – 2012-07-24 00:07:28

+0

这个为我工作!整洁和完美! – 2012-12-06 06:53:20

0

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

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

参见Filenames and Pathnames in Shell