是否可以安全地使用`find -exec sh -c`?

Mar*_*ich 40 shell find

我正在尝试将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 -cfind -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{}zshzargs -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. 您应该使用相关的有什么(如shfind-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)

  • @FlorianFida 外壳的第一个参数变为`$0`(通常是外壳的名称。在这种情况下您需要跳过它,这样它就不会占用您的正常位置参数之一。`-c` 的文档中提到了这一点. (2认同)
  • @EtanReisner http://www.in-ulm.de/~mascheck/various/find/#embedded (2认同)