我正在尝试将findtoecho 0用于某些文件,但显然这仅适用于sh -c:
find /proc/sys/net/ipv6 -name accept_ra -exec sh -c 'echo 0 > {}' \;
Run Code Online (Sandbox Code Playgroud)
但是使用sh -cwithfind -exec让我觉得很不安,因为我怀疑引用问题。我稍微摆弄了一下,显然我的怀疑是有道理的:
我的测试设置:
martin@dogmeat ~ % cd findtest
martin@dogmeat ~/findtest % echo one > file\ with\ spaces
martin@dogmeat ~/findtest % echo two > file\ with\ \'single\ quotes\'
martin@dogmeat ~/findtest % echo three > file\ with\ \"double\ quotes\"
martin@dogmeat ~/findtest % ll
insgesamt 12K
-rw-rw-r-- 1 martin martin 6 Sep 17 12:01 file with "double quotes"
-rw-rw-r-- 1 martin martin 4 Sep 17 12:01 file with 'single quotes'
-rw-rw-r-- 1 martin martin 4 Sep 17 12:01 file with spaces
Run Code Online (Sandbox Code Playgroud)使用find -execwithoutsh -c似乎没有问题 - 这里不需要引用:
martin@dogmeat ~ % find findtest -type f -exec cat {} \;
one
two
three
Run Code Online (Sandbox Code Playgroud)但是当我使用时sh -c {}似乎需要某种引用:
martin@dogmeat ~ % LANG=C find findtest -type f -exec sh -c 'cat {}' \;
cat: findtest/file: No such file or directory
cat: with: No such file or directory
cat: spaces: No such file or directory
cat: findtest/file: No such file or directory
cat: with: No such file or directory
cat: single quotes: No such file or directory
cat: findtest/file: No such file or directory
cat: with: No such file or directory
cat: double quotes: No such file or directory
Run Code Online (Sandbox Code Playgroud)只要没有文件名包含双引号,双引号就可以工作:
martin@dogmeat ~ % LANG=C find findtest -type f -exec sh -c 'cat "{}"' \;
one
two
cat: findtest/file with double: No such file or directory
cat: quotes: No such file or directory
Run Code Online (Sandbox Code Playgroud)只要没有文件名包含单引号,单引号就可以工作:
martin@dogmeat ~ % LANG=C find findtest -type f -exec sh -c "cat '{}'" \;
one
cat: findtest/file with single: No such file or directory
cat: quotes: No such file or directory
three
Run Code Online (Sandbox Code Playgroud)我还没有找到适用于所有情况的解决方案。有没有我忽略的东西,或者sh -c在find -exec本质上不安全的东西?
Sté*_*las 53
永远不要嵌入{}到 shell 代码中!这会创建一个命令注入漏洞。请注意,对于cat "{}",它不仅与"字符有关\,`,$也是一个问题(例如,考虑一个名为./$(reboot)/accept_ra)¹的文件。
(顺便说一下,有些find实现不会让你做到这一点,和POSIX离开的行为未指定的时候{}是不是对自己的一个参数find)
在这里,您希望将文件名作为单独的参数传递给sh(不在代码参数中)和sh内联脚本(代码参数)以使用位置参数来引用它们:
find . -name accept_ra -exec sh -c 'echo 0 > "$1"' sh {} \;
Run Code Online (Sandbox Code Playgroud)
或者,为了避免sh每个文件运行一个:
find . -name accept_ra -exec sh -c 'for file do
echo 0 > "$file"; done' sh {} +
Run Code Online (Sandbox Code Playgroud)
这同样适用于xargs -I{}或zsh的zargs -I{}。不要写:
<list.txt xargs -I{} sh -c 'cmd > {}'
这将是一个与find上面相同的命令注入漏洞,但是:
<list.txt xargs sh -c 'for file do cmd > "$file"; done' sh
Run Code Online (Sandbox Code Playgroud)
它还具有避免sh为每个文件运行一个以及list.txt不包含任何文件时的错误的好处。
使用zsh's zargs,您可能想要使用函数而不是调用sh -c:
do-it() cmd > $1
zargs ./*.txt -- do-it
Run Code Online (Sandbox Code Playgroud)
请注意,在上面的所有示例中,上面的第二个sh都进入了内联脚本的$0. 您应该使用相关的有什么(如sh或find-sh),不喜欢的东西_,-,--或空字符串,如值$0用于壳的错误信息:
$ find . -name accept_ra -exec sh -c 'echo 0 > "$1"' inline-sh {} \;
inline-sh: ./accept_ra: Permission denied
Run Code Online (Sandbox Code Playgroud)
GNU 的parallel工作方式不同。有了它,你就不会想用sh -c作为parallel不运行shell已经和尝试,以取代{}在shell的语法正确引述的说法。
<list.txt PARALLEL_SHELL=sh parallel 'cmd > {}'
Run Code Online (Sandbox Code Playgroud)
¹ 并且根据sh实现,其他字符的编码包含这些字符的编码(在实践中和现实生活中的字符编码,仅限于字节 0x5c 和 0x60)