我有几个目录,其名称如下所示:
我想递归地从末尾删除换行符。
我检查了递归重命名目录
我也检查过删除文件名中的换行符
它建议的解决方案是:
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*'
n
second
-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
以检查您的系统。