我试图在某些文件中使用find
to echo 0
,但显然这只适用于sh -c
:
find /proc/sys/net/ipv6 -name accept_ra -exec sh -c 'echo 0 > {}' \;
但是使用sh -c
withfind -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
使用
find -exec
withoutsh -c
似乎工作没有问题 - 这里不需要引用:martin@dogmeat ~ % find findtest -type f -exec cat {} \; one two three
但是当我使用时
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
只要文件名不包含双引号,双引号就起作用:
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
只要文件名不包含单引号,单引号就起作用:
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
我还没有找到适用于所有情况的解决方案。有什么我忽略的东西,或者使用sh -c
本质find -exec
上不安全的东西吗?
答案1
切勿嵌入{}
shell 代码!这会产生命令注入漏洞。请注意,对于cat "{}"
,它不仅与"
字符有关,\
, `
,$
也是一个问题(例如,考虑一个名为 的文件./$(reboot)/accept_ra
)。
(顺便一提,一些find
实现不会让你这样做,并且POSIX 留下行为未指定当{}
不是单独存在于find -exec
)的论证中
在这里,您希望将文件名作为单独的参数传递给sh
(而不是在代码参数)和sh
内联脚本(代码argument) 使用位置参数来引用它们:
find . -name accept_ra -exec sh -c 'echo 0 > "$1"' sh {} \;
或者,为了避免sh
每个文件运行一个:
find . -name accept_ra -exec sh -c 'for file do
echo 0 > "$file"; done' sh {} +
这同样适用于xargs -I{}
or 。不要写:zsh
zargs -I{}
<list.txt xargs -I{} sh -c 'cmd > {}'
这将是一个命令注入漏洞,与上面的方式相同find
,但是:
<list.txt xargs sh -c 'for file do cmd > "$file"; done' sh
这还有一个好处,可以避免每个文件运行一个,以及避免在不包含任何文件sh
时出现错误。list.txt
对于zsh
s zargs
,您可能想要使用函数而不是调用sh -c
:
do-it() cmd > $1
zargs -l1 ./*.txt -- do-it
尽管您也可以使用for
zsh 中的循环,该循环可能非常短:
for f (*.txt) cmd > $f
(不过,除了最后一个之外的失败cmd
不会在总体退出状态中报告)。
请注意,在上面的所有示例中,sh
上面的第二个都进入内联脚本的$0
.您应该在那里使用相关的内容(例如sh
或find-sh
),而不是像_
、-
或--
空字符串这样的内容,因为 in 中的值$0
用于 shell 的错误消息:
$ find . -name accept_ra -exec sh -c 'echo 0 > "$1"' inline-sh {} \;
inline-sh: ./accept_ra: Permission denied
GNU 的parallel
工作方式不同。有了它,你就可以不是想要sh -c
用作parallel
已经运行 shell 并尝试替换{}
为 shell 的正确语法中引用的参数。
<list.txt PARALLEL_SHELL=sh parallel 'cmd > {}'
以及根据实现,sh
其他字符的编码包含这些字符的编码(在实践中和现实生活中的字符编码,仅限于字节 0x5c 和 0x60)