Shell

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

  • March 26, 2022

我正在嘗試將findtoecho 0用於某些文件,但顯然這只適用於sh -c

find /proc/sys/net/ipv6 -name accept_ra -exec sh -c 'echo 0 > {}' \;

但是使用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
  • 使用find -execwithoutsh -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 -cfind -exec本質上是不安全的?

永遠不要嵌入{}到 shell 程式碼中!這會產生命令注入漏洞。請注意,對於cat "{}",它不僅與"字元有關,\,```也是$一個問題(例如,考慮一個名為 的文件./$(reboot)/accept_ra)¹。

(順便說一句,某些find實現不會讓您這樣做,並且POSIX{}未在參數中單獨指定行為時find -exec

在這裡,您希望將文件名作為單獨的參數傳遞給sh(而不是在程式碼參數中),並通過sh內聯腳本(程式碼參數)使用位置參數來引用它們:

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 zshzargs -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 ./*.txt -- do-it

請注意,在上面的所有範例中,上面第二個sh都進入了內聯腳本的$0. 您應該在此處使用相關的內容(例如shor find-sh),而不是 , 或空字元串之類_的內容,因為 in 的值-用於shell 的錯誤消息:--``$0

$ find . -name accept_ra -exec sh -c 'echo 0 > "$1"' inline-sh {} \;
inline-sh: ./accept_ra: Permission denied

GNUparallel的工作方式不同。有了它,您不想使用sh -cas paralleldoes 已經執行 shell 並嘗試{}用正確語法中引用的參數替換 shell

<list.txt PARALLEL_SHELL=sh parallel 'cmd > {}'

¹ 並且根據sh實現,其他字元的編碼包含這些字元的編碼(在實踐中和現實生活中的字元編碼,僅限於字節 0x5c 和 0x60)

引用自:https://unix.stackexchange.com/questions/156008