使用“find”转义文件名上的异常字符。 -printf“%p\n”`

使用“find”转义文件名上的异常字符。 -printf“%p\n”`

来自此find命令的文件路径

find . -printf "%p \n"

不要转义不常见的字符(空格、反斜杠、双引号...)。

-ls选项确实打印转义路径,但它只是将 的输出添加ls -dils到 的输出之前printf

我需要一个高效的命令,因此运行额外的命令ls没有帮助,打印出所有额外的字符也没有帮助。

还有其他(优雅的)方法来输出转义路径吗find

答案1

通常,您希望使用find -exec对所有文件名运行命令,或者find -print0将名称通过管道传递给某个可以读取由 nul 字节分隔的条目的命令(例如xargs -0)。

如果你确实想要带引号的字符串,Bash 有几个选项可以做到这一点:

$ find -exec bash -c 'printf "%s\n" "${@@Q}"' sh {} +
'./single'\''quote'
'./space 1'
$'./new\nline'
'./double"quote'

$ find -exec bash -c 'printf "%q\n" "$@"' sh {} +
./single\'quote
./space\ 1
$'./new\nline'
./double\"quote

这确实需要额外调用 shell,但可以使用一次 exec 处理多个文件名。


关于保存权限位(不是 ACL),你可以这样做(在 GNU find 中):

find -printf "%#m:%p\0" > files-and-modes

这将输出带有权限、冒号、文件名和 nul 字节的条目,例如:。它会0644:name with spaces\0不是转义任何内容,而是按原样打印文件名(除非输出到终端,在这种情况下至少换行符会被破坏。)

您可以使用 Perl 脚本读取结果:

perl -0 -ne '($m, $f) = split/:/, $_, 2; chmod oct($m), $f; ' < files-and-modes 

或者勉强在 Bash 中,请参阅评论:

while IFS=: read -r -d '' mode file ; do
    # do something useful
    printf "<%s> <%s>\n" "$mode" "$file"
    chmod "$mode" "$file"
done < files-and-modes

据我测试,它适用于换行符、引号、空格和冒号。请注意,我们需要使用空格以外的其他内容作为分隔符,因为IFS=" "如果任何名称包含尾随空格,设置将删除它们。

答案2

有了zsh,你可以这样做:

print -r -- ./**/*(.D:q)

.相当于-type fD包括像findwill 这样的隐藏文件,以及:q用于引用(使用zsh-style 引用,我无法判断这是否是您期望的引用类型)。

您可以获得不同风格的引用:

$ print -r -- ./**/*(.D:q)
./$'\200' ./a\ b ./é ./\"foo\" ./It\'s\ bad ./$'\n'
$ files=(./**/*(.D))
$ print -r -- ${(q)files}
./$'\200' ./$'\n' ./a\ b ./é ./\"foo\" ./It\'s\ bad
$ print -r -- ${(qq)files}
'./�' './
' './a b' './é' './"foo"' './It'\''s bad'
$ print -r -- ${(qqq)files}
"./�" "./
" "./a b" "./é" "./\"foo\"" "./It's bad"
$ print -r -- ${(qqqq)files}
$'./\200' $'./\n' $'./a b' $'./é' $'./"foo"' $'./It\'s bad'

是我的终端模拟器为不可打印的 \200 字节显示的占位符)。

在这里,如果您希望能够以可恢复的方式存储权限,只需执行以下操作:

find . -type f -printf '%m\0%p\0' > saved-permissions

要恢复(假设 GNU xargs):

xargs -r0n2 -a saved-permissions chmod

然而,这将为chmod每个文件运行一次调用,这是非常低效的。您可能想使用chmod内置的shell,zsh如下所示zmodload zsh/files

zmodload zsh/files
while IFS= read -rd '' mode && IFS= read -rd '' file; do
  chmod $mode $file
done < saved-permissions

答案3

我找到了一个更简单的解决方案:

find -exec echo \'{}\' \;

我们可以使用它来管道到任何其他程序:

find -exec echo \'{}\' \; | xargs ls 

或者我们可以简单地使用任何命令而不是 echo 并添加任意数量的参数:

find -exec mv \'{}\' somewhereelse \;

不要忘记echo首先进行测试。

最后一个不起作用因为 mv 在访问文件之前不会删除引号,所以请改用管道:

find -exec echo mv \'{}\' somewhereelse \;| bash

答案4

为什么不转义每个字符?然后它适用于任何东西(ls、rm、echo 等):

find -name *.txt | sed -E 's/(.)/\\\1/g' | xargs ls

(您可能需要将其适应-exec形式,以便变得更有效)

相关内容