谁能推荐一个安全的解决方案来递归地用下划线替换从给定根目录开始的文件和目录名中的空格?例如:

$ tree
.
|-- a dir
|   `-- file with spaces.txt
`-- b dir
    |-- another file with spaces.txt
    `-- yet another file with spaces.pdf

就变成:

$ tree
.
|-- a_dir
|   `-- file_with_spaces.txt
`-- b_dir
    |-- another_file_with_spaces.txt
    `-- yet_another_file_with_spaces.pdf

当前回答

这是一个(相当冗长的)find -exec解决方案,它将“文件已经存在”警告写入stderr:

function trspace() {
   declare dir name bname dname newname replace_char
   [ $# -lt 1 -o $# -gt 2 ] && { echo "usage: trspace dir char"; return 1; }
   dir="${1}"
   replace_char="${2:-_}"
   find "${dir}" -xdev -depth -name $'*[ \t\r\n\v\f]*' -exec bash -c '
      for ((i=1; i<=$#; i++)); do
         name="${@:i:1}"
         dname="${name%/*}"
         bname="${name##*/}"
         newname="${dname}/${bname//[[:space:]]/${0}}"
         if [[ -e "${newname}" ]]; then
            echo "Warning: file already exists: ${newname}" 1>&2
         else
            mv "${name}" "${newname}"
         fi
      done
  ' "${replace_char}" '{}' +
}

trspace rootdir _

其他回答

find . -depth -name '* *' \
| while IFS= read -r f ; do mv -i "$f" "$(dirname "$f")/$(basename "$f"|tr ' ' _)" ; done

一开始我没有把它弄好,因为我没有想到目录。

使用rename(又名prename),这是一个Perl脚本,可能已经在您的系统上了。分两步:

find . -name "* *" -type d | rename 's/ /_/g'    # do the directories first
find . -name "* *" -type f | rename 's/ /_/g'

基于Jürgen的回答,能够使用“Revision 1.5 1998/12/18 16:16:31 rmb1”版本的/usr/bin/rename (Perl脚本)在单一边界内处理多层文件和目录:

find /tmp/ -depth -name "* *" -execdir rename 's/ /_/g' "{}" \;

这个做得更多一些。我用它来重命名我下载的种子文件(没有特殊字符(非ascii),空格,多个点等)。

#!/usr/bin/perl

&rena(`find . -type d`);
&rena(`find . -type f`);

sub rena
{
    ($elems)=@_;
    @t=split /\n/,$elems;

    for $e (@t)
    {
    $_=$e;
    # remove ./ of find
    s/^\.\///;
    # non ascii transliterate
    tr [\200-\377][_];
    tr [\000-\40][_];
    # special characters we do not want in paths
    s/[ \-\,\;\?\+\'\"\!\[\]\(\)\@\#]/_/g;
    # multiple dots except for extension
    while (/\..*\./)
    {
        s/\./_/;
    }
    # only one _ consecutive
    s/_+/_/g;
    next if ($_ eq $e ) or ("./$_" eq $e);
    print "$e -> $_\n";
    rename ($e,$_);
    }
}

查找/重命名解决方案。Rename是util-linux的一部分。

你需要先降低深度,因为一个空白文件名可以是空白目录的一部分:

find /tmp/ -depth -name "* *" -execdir rename " " "_" "{}" ";"

对于那些使用macOS的人来说,首先安装所有的工具:

 brew install tree findutils rename

然后当需要重命名时,将GNU find (gfind)的别名改为find。然后运行@Michel Krelin的代码:

alias find=gfind 
find . -depth -name '* *' \
| while IFS= read -r f ; do mv -i "$f" "$(dirname "$f")/$(basename "$f"|tr ' ' _)" ; done