递归地从目录名称中删除换行符

递归地从目录名称中删除换行符

我有几个目录,其名称如下所示:

在此输入图像描述

我想递归地从末尾删除换行符。

我检查了递归重命名目录

我也检查过删除文件名中的换行符

它建议的解决方案是:

find -name $'*\n*' -exec rename  $'s|\n| |g' '{}' \;

但就我而言,find -name $'*\n*'什么也没有返回。如果我删除它$可以找到目录

% find . -name '*\n*'
./second?
% find . -name '*\r*'
./third?
./first?

但是,当我运行时find . -name '*\n*' -exec rename $'s|\n| |g' '{}' \;它不会重命名目录。我也尝试过find . -name $'*\n*' -exec rename $'\n' ' ' {} \;过从递归删除文件名中的换行符。它也没有重命名目录。

我能做些什么?

答案1

你之前这么说

printf "%q\n" *节目first$'\342\200\251' second$'\342\200\251' third$'\342\200\251'

$'\342\200\251'常规的换行符,而是统一码 U+2029 段落分隔符,以 UTF-8 编码。那里的输出有八进制的字节;以十六进制表示,它们将是e2 80 a9

这就是为什么find -name $'*\n*'不匹配它。如果没有美元,则与仅匹配中的字母-name '*\n*'相同。这些模式只是 shell 全局模式,这使得下一个字符不特殊。例如,仅匹配单个星号,而匹配任何内容。并不特殊,所以就和它一样。-name '*n*'nsecond-name\\**n\n

鉴于我们现在知道它是什么,我们可以像删除换行符一样删除它。通过 Perl 重命名(例如使用File::Rename),这应该删除它们:

find . -depth -name $'*\342\200\251' \
       -execdir rename -v $'s|\342\200\251||g' '{}' +

您需要-depth在分支所在的分支之前重命名叶子,并且-execdir(不是标准的,但很常见)rename从包含文件的目录中调用,仅使用文件的基名进行重命名。

... rename -v 's|\342\200\251||g'应该也可以工作,因为它是一个 Perl 表达式并且 Perl解释反斜杠也会自行转义。

您所拥有的命令rename $'s|\n| |g'将替换为空格,但由于名称末尾有字符,这也会令人困惑。

答案2

zsh

autoload zmv # best in ~/.zshrc
zmv -v $'(**/)(*[\n\u2029]*)(#qD)' $'$1${2//[\u2029\n]}'

要删除两个换行符 (U+000A) 或段落分隔符字符(U+2029)。

或者:

zmv -v $'(**/)(*[[:cntrl:]]*)(#qD)' $'$1${2//[[:cntrl:]]}'

删除所有控制字符。

不过 U+2029 是否会被分类cntrl将取决于系统。它适用于 Ubuntu 20.04,但不适用于 FreeBSD 12.2。运行[[ $'\u2029' = [[:cntrl:]] ]] && echo yes以检查您的系统。

相关内容