Snažím se použít find
na echo 0
do některých souborů, ale zřejmě to funguje pouze s sh -c
:
find /proc/sys/net/ipv6 -name accept_ra -exec sh -c 'echo 0 > {}' ;
Ale pomocí sh -c
pomocí find -exec
cítím se velmi neklidně, protože mám podezření na problémy s citováním. Trochu jsem si s tím pohrál a zřejmě mé podezření bylo oprávněné:
-
Moje testovací nastavení:
[email protected] ~ % cd findtest [email protected] ~/findtest % echo one > file with spaces [email protected] ~/findtest % echo two > file with 'single quotes' [email protected] ~/findtest % echo three > file with "double quotes" [email protected] ~/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
-
Pomocí
find -exec
bezsh -c
Zdá se, že funguje bez problémů – zde není třeba citovat:[email protected] ~ % find findtest -type f -exec cat {} ; one two three
-
Ale když používám
sh -c
{}
Zdá se, že vyžaduje nějaký druh citace:[email protected] ~ % 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
-
Dvojité uvozovky fungují, pokud žádný název souboru neobsahuje uvozovky:
[email protected] ~ % 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
-
Jednoduché uvozovky fungují, pokud žádný název souboru neobsahuje jednoduché uvozovky:
[email protected] ~ % 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
Nenašel jsem řešení, které by fungovalo ve všech případech. Je něco, co přehlížím, nebo používám sh -c
v find -exec
ze své podstaty nebezpečné?
Přijatá odpověď:
Nikdy nevkládejte {}
v kódu shellu! To vytváří zranitelnost příkazového vkládání. Všimněte si, že pro cat "{}"
, není to jen o "
znaků, ,
`
, $
jsou také problémem (uvažte například soubor s názvem ./$(reboot)/accept_ra
)¹.
Zde byste chtěli předat názvy souborů jako samostatné argumenty do sh
(není v kódu argument) a sh
vložený skript (kód argument), abyste na ně odkazovali pomocí pozičních parametrů:
find . -name accept_ra -exec sh -c 'echo 0 > "$1"' sh {} ;
Nebo, abyste se vyhnuli spuštění jednoho sh
na soubor:
find . -name accept_ra -exec sh -c 'for file do
echo 0 > "$file"; done' sh {} +
Totéž platí pro xargs -I{}
nebo zsh
's zargs -I{}
. Nepište:
<list.txt xargs -I{} sh -c 'cmd > {}'
Což by byla zranitelnost vkládání příkazů stejným způsobem jako u find
výše, ale:
<list.txt xargs sh -c 'for file do cmd > "$file"; done' sh
Což má také tu výhodu, že se vyhnete spuštění jednoho sh
na soubor a chyba při list.txt
neobsahuje žádný soubor.
Pomocí zsh
's zargs
, pravděpodobně budete chtít použít funkci místo volání sh -c
:
do-it() cmd > $1
zargs ./*.txt -- do-it
Všimněte si, že ve všech příkladech výše je druhý sh
výše přejde do $0
vloženého skriptu . Měli byste tam použít něco relevantního (například sh
nebo find-sh
), ne věci jako _
, -
, --
nebo prázdný řetězec jako hodnotu v $0
se používá pro chybové zprávy shellu:
$ find . -name accept_ra -exec sh -c 'echo 0 > "$1"' inline-sh {} ;
inline-sh: ./accept_ra: Permission denied
GNU parallel
funguje jinak. S ním ne chcete použít sh -c
jako parallel
již spouští shell a pokouší se nahradit {}
s argumentem citovaným ve správné syntaxi pro shell.
<list.txt PARALLEL_SHELL=sh parallel 'cmd > {}'