一部のファイルでfind
からecho 0
を使用しようとしていますが、これはsh -c
でのみ機能するようです:
find /proc/sys/net/ipv6 -name accept_ra -exec sh -c 'echo 0 > {}' \;
しかし、sh -c
をfind -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
なしでsh -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
を本質的に安全ではありませんか?
シェルコードに_{}
_を埋め込まないでください!これにより、コマンドインジェクションの脆弱性が生まれます。 _cat "{}"
_の場合、_"
_文字だけではなく、_\
_、_`
_、_$
_も問題であることに注意してください(たとえば、./$(reboot)/accept_ra
)。
(ちなみに、 some find
implementations はそれを許可しません、そして POSIXは動作を残します未指定 _{}
_がfind
への引数に単独ではない場合
ここでは、ファイル名を個別の引数としてsh
(code引数ではなく)に渡し、sh
インラインに渡します。位置パラメータを使用してそれらを参照するスクリプト(code引数):
_find . -name accept_ra -exec sh -c 'echo 0 > "$1"' sh {} \;
_
または、ファイルごとに1つのsh
を実行しないようにするには:
_find . -name accept_ra -exec sh -c 'for file do
echo 0 > "$file"; done' sh {} +
_
同じことが_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
_
また、_list.txt
_にファイルが含まれていない場合、ファイルごとに1つのsh
とエラーを実行しないようにする利点もあります。
zsh
のzargs
を使用すると、_sh -c
_を呼び出すのではなく、関数を使用することになります。
_do-it() cmd > $1
zargs ./*.txt -- do-it
_
上記のすべての例で、上記の2番目のsh
はインラインスクリプトの_$0
_に入ることに注意してください。値としては、_find-sh
_、__
_、_-
_や空の文字列などではなく、そこで関連するもの(sh
または_--
_)を使用する必要があります_$0
_は、シェルのエラーメッセージに使用されます。
_$ find . -name accept_ra -exec sh -c 'echo 0 > "$1"' inline-sh {} \;
inline-sh: ./accept_ra: Permission denied
_
GNU parallel
の動作は異なります。 parallel
はすでにシェルを実行しているので、_sh -c
_を使用したいではありません _{}
_を、シェルの正しい構文で引用された引数 に置き換えます。
_<list.txt PARALLEL_Shell=sh parallel 'cmd > {}'
_