BASH - 递归重命名包含无效/非打印字符的文件

时间:2014-07-30 03:57:13

标签: bash sed filenames rename

我根据包含该书的PDF文件中的标题手动重命名了许多文件(电子书)。我这样做是通过手动复制PDF阅读器中的多行文本,然后在Nautilus中重命名文件。我想将这些全部添加到SVN仓库中,但由于包含0x0A(换行符)字符的多个PDF的文件名本身,add命令失败。我的语言环境在我的.bashrc文件中设置为UTF8,似乎Ubuntu的Nautilus文件资源管理器实用程序允许我将非打印字符粘贴到文件名中(是否可以禁用此功能吗)。

无论如何,我现在有一个包含子目录,PDF,PDF子目录等子目录的大型目录。是否有办法以递归方式遍历目录结构并从文件名中删除任何非打印字符(即:换行符)?

我尝试了以下操作来遍历文件(,其名称中包含空格):

#!/bin/bash
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
for f in *
do
  echo "Renaming $f"
  mv ${f} $(echo ${f} | sed 's/\n//g')
done
IFS=$SAVEIFS

但是,文件名中带有换行符的文件将打印在两个单独的行上,就好像它们是单独的条目一样。我在SO(sed command to fix filenames in a directory)上找到了一个可能的解决方案,但只有当所有文件都在同一个目录中时才有效,而不是我目前拥有的大子目录结构。

谢谢。

1 个答案:

答案 0 :(得分:5)

您不需要使用IFS。只需将您的参数括在""左右,以防止分词:

mv "${f}" "$(echo "${f}" | sed 's/\n//g')"

此外,您可以使用特殊参数扩展来删除换行符:

mv "${f}" "${f//$'\n'}"

请参阅Word SplittingParameter Expansion

注意:只有开放变量受IFS影响。 <{1}}当它们展开时的立即全局模式不会分裂。

要使用globs启用递归,请启用*globstar。然后就可以了

shopt -s globstar

使用for f in /path/to/dir/**; do [[ ! -d $f ]] && mv "$f" "${f//$'\n'}" ## Test lets it process files only. done

find

与使用流程替换相同:

find -type f '/path/to/dir' -print0 | while IFS= read -rd '' f; do
    mv "$f" "${f//$'\n'}"
done

使用while IFS= read -rd ''; do mv "$f" "${f//$'\n'}" done < <(exec find -type f '/path/to/dir' -print0) IFS=会禁用输入中的单词拆分。 read禁用解释反斜杠引号,-r将分隔符设置为-d ''。它适用于0x00,它将find设置为输出分隔符,而不是带0x00的换行符(0x0A)。

也可以使用字符集:

-print0

你可能想要:

[:alpha:]   Alphabetic characters.
[:blank:]   Space and TAB characters.
[:cntrl:]   Control characters.
[:digit:]   Numeric characters.
[:graph:]   Characters that are both printable and visible.
[:lower:]   Lowercase alphabetic characters.
[:print:]   Printable characters (characters that are not control characters).
[:punct:]   Punctuation characters (characters that are not letters, digits,
[:space:]   Space characters (such as space, TAB, and formfeed, to name a few).
[:upper:]   Uppercase alphabetic characters.
[:xdigit:]  Characters that are hexadecimal digits.

或者

mv "$f" "${f//[[:cntrl:]]}"

你也可以加入他们:

mv "$f" "${f//[^[:print:]]}"  ## Does not only include control chars but probably some if not all extended chars as well.

当然在进行实际运行之前先测试它们:

mv "$f" "${f//[[:cntrl:]|!@#$%^&*()]}"