递归地“规范化”文件名

时间:2011-01-17 20:59:24

标签: linux bash sh

我的意思是摆脱文件名中的特殊字符等。

我制作了一个脚本,可以递归重命名文件[http://pastebin.com/raw.php?i=kXeHbDQw]:

例如:之前:

THIS i.s my file (1).txt
运行脚本后

This-i-s-my-file-1.txt

确定。这是:

但是:当我想“完全”测试它时,文件名如下:

¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÂÃÄÅÆÇÈÊËÌÎÏÐÑÒÔÕ×ØÙUÛUÝÞßàâãäåæçèêëìîïðñòôõ÷øùûýþÿ.txt
áíüűúöőóéÁÍÜŰÚÖŐÓÉ!"#$%&'()*+,:;<=>?@[\]^_`{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£.txt

它失败[http://pastebin.com/raw.php?i=iu8Pwrnr]:

$ sh renamer.sh directorythathasthefiles
mv: cannot stat `./áíüűúöőóéÁÍÜŰÚÖŐÓÉ!"#$%&\'()*+,:;<=>?@[]^_`{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£': No such file or directory
mv: cannot stat `./áíüűúöőóéÁÍÜŰÚÖŐÓÉ!"#$%&\'()*+,:;<=>?@[]^_`{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£': No such file or directory
mv: cannot stat `./áíüűúöőóéÁÍÜŰÚÖŐÓÉ!"#$%&\'()*+,:;<=>?@[]^_`{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£': No such file or directory
mv: cannot stat `./áíüűúöőóéÁÍÜŰÚÖŐÓÉ!"#$%&\'()*+,:;<=>?@[]^_`{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£': No such file or directory
mv: cannot stat `./áíüűúöőóéÁÍÜŰÚÖŐÓÉ!"#$%&\'()*+,:;<=>?@[]^_`{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£': No such file or directory
mv: cannot stat `./áíüűúöőóéÁÍÜŰÚÖŐÓÉ!"#$%&\'()*+,:;<=>?@[]^_`{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£': No such file or directory
mv: cannot stat `./áíüűúöőóéÁÍÜŰÚÖŐÓÉ!"#$%&\'()*+,:;<=>?@[]^_`{|}~€‚ƒ„…†....and so on
$

所以“mv”无法处理特殊字符..:\

我工作了好几个小时..

有人有工作吗? [也可以处理2行中的字符[文件名]?]

4 个答案:

答案 0 :(得分:18)

mv处理特殊字符就好了。你的剧本没有。


没有特别的顺序:

  1. 您正在使用find分别查找所有目录和ls每个目录。

    1. 为什么使用for DEPTH in...如果 命令完全

      find -maxdepth 100 -type d
      
    2. 这使得不需要任意深度限制

      find -type d
      
    3. 永远解析ls尤其是的输出,如果你也可以find处理 p>

      find -not -type d
      
    4. 确保它在最糟糕的情况下有效:

      find -not -type d -print0 | while read -r -d '' FILENAME; do
      

      这会阻止read吃掉某些转义符并使用换行符阻塞文件名。

  2. 您正在为每个字符重复整个 ls | replace周期。 不要 - 它会影响性能。遍历每个目录所有文件一次,并在一个sed命令中使用多个sed或多个替换。

    sed 's/á/a/g; s/í/i/g; ...'
    

    (我打算建议sed 'y/áí/ai/',但不幸的是,这似乎不适用于Unicode。也许perl -CS -Mutf8 -pe 'y/áí/ai/'会。)

  3. 您仍在考虑ASCII:“其他特殊字符 - ASCII代码33 .. .255”。不。

    1. 目前,大多数系统都使用UTF-8编码的Unicode,它具有范围更广的“特殊”字符 - 如此之大以至于逐个列出它们变得毫无意义。 (甚至多字节 - “e”是一个字节,“ė”是三个字节。)

    2. True ASCII有128个字符。您目前的想法是ISO 8859字符集(有时称为“ANSI”) - 特别是ISO 8859-1。但它们一直到8859-16,只有“ASCII”部分保持不变。

  4. echo -n $(command)毫无用处。

  5. 在给定路径的情况下,有更简单的方法可以找到目录和基本名称。例如,你可以做

    directory=$(dirname "$path")
    oldnname=$(basename "$path")
    # filter $oldname
    mv "$path" "$directory/$newname"
    
  6. 使用egrep检查错误。检查程序的返回代码。 (就像你已经使用cd一样。)

  7. 而不是过滤掉其他错误,请执行...

    if [[ -e $directory/$newname ]]; then
        echo "target already exists, skipping: $oldname -> $newname"
        continue
    else
        mv "$path" "$directory/$newname"
    fi
    
  8. 可以将sed 's/------------/-/g'次调用更改为单个正则表达式:

    sed -r 's/-{2,}/-/g'
    
  9. [ ]中的tr [foo] [bar]是不必要的。他们只需tr[替换为[,将]替换为]

  10. 真的?

    echo "$FOLDERNAME" | sed "s/$/\//g"
    

    相反怎么样?

    echo "$FOLDERNAME/"
    

  11. 最后,使用detox

答案 1 :(得分:6)

尝试类似:

find . -print0 -type f | awk 'BEGIN {RS="\x00"} { printf "%s\x00", $0; gsub("[^[:alnum:]]", "-"); printf "%s\0", $0 }' | xargs -0 -L 2 mv

使用xargs(1)将确保每个文件名完全作为一个参数传递。 awk(1)用于在旧文件名之后添加新文件名。

还有一个技巧:sed -e's / - + / - / g'将只用一个“ - ”代替一组“ - ”。

答案 2 :(得分:4)

假设您的其他脚本是正确的,那么您的问题是您使用的是read,但您应该使用read -r。注意反斜杠是如何消失的:

áíüűúöőóéÁÍÜŰÚÖŐÓÉ!"#$%&'()*+,:;<=>?@[\]^_`{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£.txt
áíüűúöőóéÁÍÜŰÚÖŐÓÉ!"#$%&\'()*+,:;<=>?@[]^_`{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£

答案 3 :(得分:1)

啊...

清理脚本的一些提示:

**使用sed一次对多个字符进行翻译,这样可以清理并简化管理:

dev:~$ echo 'áàaieeé!.txt' | sed -e 's/[áàã]/a/g; s/[éè]/e/g'
aaaieee!.txt

**而不是重命名每次更改的文件,运行所有过滤器,然后执行一次移动

$ NEWNAME='áàaieeé!.txt'
$ NEWNAME="$(echo "$NEWNAME" | sed -e 's/[áàã]/a/g; s/[éè]/e/g')"
$ NEWNAME="$(echo "$NEWNAME" | sed -e 's/aa*/a/g')"
$ echo $NEWNAME
aieee!.txt

**而不是进行ls | read ...循环,请使用:

for OLDNAME in $DIR/*; do
  blah
  blah
  blah
done

**分离出你的路径遍历并将逻辑重命名为两个脚本。一个脚本找到需要重命名的文件,一个脚本处理单个文件的规范化。一旦你学会了'find'命令,你就会发现你可以抛出第一个脚本:)