2012-07-17 72 views
4

我一直在试图递归重命名文件和文件夹与iconv没有成功,文件正确地重命名,但文件夹不。如何从Bash递归重命名文件和文件夹与iconv

我用什么文件是(完美的作品):

find . -name * -depth \ -exec bash -c 'mv "$1" "${1%/*}/$(iconv -f UTF8 -t ASCII//TRANSLIT <<< ${1##*/})"' -- {} \; 

我尝试了文件和文件夹(失败:只重命名文件夹):

find . -exec bash -c 'mv "$1" "$(iconv -f UTF8 -t ASCII//TRANSLIT <<< $1)"' -- {} \; 

原来的问题: 我只是想批量重命名很多文件,使他们“网络友好”,认为像删除空间,怪异的字符等,目前我有

find . -name '*' -depth \ 
| while read f ; 
do 
mv -i "$f" "$(dirname "$f")/$(basename "$f"|tr -s ' ' _|tr -d "'"|tr -d ","|tr - _|tr "&" "y"|tr "@" "a")" ; 
done 

有没有什么办法可以在单次运行时执行上面的tr和iconv?因为我正在谈论300,000个文件重新命名,如果可能,我想避免第二次搜索。

如果需要,我正在使用Bash 4.2.24

在此先感谢。

+0

如果你身边的perl,你也许可以使用[convmv(https://www.j3e.de/linux/ convmv /)代替(这是一个工具来转换文件名中的字符集)。 – 2012-07-17 20:28:50

+0

只是要清楚,你交换文件和文件/文件夹命令? “-type f”会排除目录。 – chepner 2012-07-17 20:42:47

+0

@chepner ups,我不好,我尝试了不同的设置。没有“-type f”,只有文件夹被重命名,但没有文件。 – nxgtrturbo 2012-07-17 20:52:22

回答

2

我认为以下所有内容都可以在一次传递中完成。

# Update: if this doesn't work, use read -d '' instead 
find . -print0 | while IFS= read -d '$\000' f ; 
do 
    orig_f="$f" 
    # Below is pure bash. You can replace with tr if you like 
    # f="$(echo $f | tr -d ,\' | tr "$'&'@- " "ya__")" 
    f="${f// /_}" # Replace spaces with _ 
    f="${f//\'}" # Remove single quote 
    f="${f//-/_}" # Replace - with _ 
    f="${f//,}" # Remove commas 
    f="${f//&/y}" # Replace ampersand with y 
    f="${f//@/a}" # Replace at sign with a 
    f=$(iconv -f UTF8 -t ASCII//TRANSLIT <<< "$f") 
    new_dir="$(dirname $f)" 
    new_f="$(basename $f)" 
    mkdir -p "$new_dir" 
    mv -i "$orig_f" "$new_dir/$new_f" 
done 

find命令(无需真正的选择,比其他-print0用空格来处理的文件名)将空分隔的文件名发送到while环路(有人会出现纠正我的错误,毫无疑问)。一长串利用参数扩展的分配将删除/替换各种字符;我使用tr作为评论,包括我认为是等效的流水线。然后我们通过iconv运行文件名来处理字符集问题。最后,我们将名称分解为其路径和文件名组件,因为在执行mv之前我们可能必须创建一个新目录。

+0

我使用此行: mv -i“$ f”“$(dirname”$ f“)/ $(basename”$ f“| tr -s''_ | tr -d”'“| tr -d “,”| tr - _ | tr“&”“y”| tr“@”“a”)“; 要成功重命名目录和文件而不创建额外的目录和文件,因为我有大约10,000个目录,这种方法会创建(在最坏的情况下)另一个10,000的权利? – nxgtrturbo 2012-07-17 21:20:18

+0

这是真的。这真的取决于你是否想用'tr'和'iconv'处理目录名。如果没有,您可以编辑我的代码以使用'mv'命令中的原始路径,就像您现在正在做的那样。否则,您需要准备好创建新的目录。 – chepner 2012-07-17 22:02:59

+0

@chepner我似乎无法得到这个工作在Ubuntu 10.04。如果我正确地理解了它,'-print0'用空值分隔文件名,那么'read -d'$ \ 000''在每个null处终止,但是当我在文件树上运行它时什么都不会发生。如果我用'echo $ f'替换'do ... done'主体,那么我只能看到空行。我开始直接复制/粘贴你的代码,所以我不认为这是错误的。这很奇怪(GNU bash,版本4.1.5) – Thorsen 2012-07-20 21:54:54

0

这是我在chepner's answer之后提供的更新,以避免嵌套错误。将find的输出与tac相反,以便在之前对文件夹内容执行文件夹内容。这样一来,就没有必要再mkdir

echo "renaming:" 
find . -print0 | tac -s '' | while IFS= read -d '' f ; 
do 
    Odir=$(dirname "$f") # original location 
    Ofile=$(basename "$f") # original filename 
    newFile=$Ofile 
    # remove unwanted characters 
    newFile=$(echo $newFile | tr -d ",'\"?()[]{}\\!") 
    newFile="${newFile// /_}" # Replace spaces with _ 
    newFile="${newFile//&/n}" # Replace ampersand with n 
    newFile="${newFile//@/a}" # Replace at sign with a 
    newFile=$(iconv -f UTF8 -t ASCII//TRANSLIT <<< "$newFile") 
    if [[ "$Ofile" != "$newFile" ]]; then # act if something has changed 
     echo "$Odir/$Ofile to" 
     echo "$Odir/$newFile" 
     mv -i "$Odir/$Ofile" "$Odir/$newFile" 
     echo "" 
    fi 
done 
echo "done." 

享受;)