Czy można bezpiecznie używać `find -exec sh -c`?

29

Próbuję użyć finddo echo 0niektórych plików, ale najwyraźniej działa to tylko z sh -c:

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

Ale używanie sh -cz find -execsprawia, że ​​czuję się bardzo nieswojo, ponieważ podejrzewam, że mam problemy z cytowaniem. Trochę się z tym bawiłem i najwyraźniej moje podejrzenia były uzasadnione:

  • Moja konfiguracja testowa:

    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
  • Używanie find -execbez sh -cwydaje się działać bezproblemowo - nie trzeba tutaj cytować

    martin@dogmeat ~ % find findtest -type f -exec cat {} \;
    one
    two
    three
  • Ale kiedy używam sh -c {}wydaje się wymagać pewnego rodzaju cytowania:

    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
  • Podwójne cudzysłowy działają, dopóki żadna nazwa pliku nie zawiera podwójnych cudzysłowów:

    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
  • Pojedyncze cudzysłowy działają, dopóki żadna nazwa pliku nie zawiera pojedynczych cudzysłowów:

    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

Nie znalazłem rozwiązania, które działa we wszystkich przypadkach. Czy jest coś, co przeoczam lub używa sh -cz find -execnatury niebezpiecznego?

Martin von Wittich
źródło

Odpowiedzi:

40

Nigdy nie osadzaj {}w kodzie powłoki! Stwarza to podatność na wstrzykiwanie poleceń. Należy pamiętać, że cat "{}"to nie tylko o "znaki, \, `, $są również problem (pod uwagę na przykład plik o nazwie./$(reboot)/accept_ra ).

(nawiasem mówiąc, niektóre findimplementacje nie pozwalają na to, a POSIX pozostawia zachowanie nieokreślone, gdy {}nie jest samo w sobie argumentemfind )

W tym miejscu chcesz przekazać nazwy plików jako osobne argumenty sh(nie w argumencie kodu ) oraz shskrypt wbudowany ( argument kodu ), aby odwoływać się do nich przy użyciu parametrów pozycyjnych:

find . -name accept_ra -exec sh -c 'echo 0 > "$1"' sh {} \;

Lub, aby uniknąć uruchamiania jednego shpliku na plik:

find . -name accept_ra -exec sh -c 'for file do
  echo 0 > "$file"; done' sh {} +

To samo dotyczy xargs -I{}lub zsh„s zargs -I{}. Nie pisz:

<list.txt xargs -I {} sh -c 'cmd> {}'

Która byłaby podatna na wstrzykiwanie poleceń tak samo jak findpowyżej, ale:

<list.txt xargs sh -c 'for file do cmd > "$file"; done' sh

Ma to również tę zaletę, że pozwala uniknąć uruchamiania jednego shpliku na plik i błędu, gdy list.txtnie zawiera on żadnego pliku.

Z zsh's zargs, prawdopodobnie wolałbyś użyć funkcji zamiast wywoływania sh -c:

do-it() cmd > $1
zargs ./*.txt -- do-it

Zauważ, że we wszystkich powyższych przykładach drugi shpowyżej znajduje się w skrypcie wbudowanym $0. Należy użyć odpowiedniego coś tam (jak shlub find-sh) nie lubią rzeczy _, -, --lub pusty łańcuch jako wartości $0stosuje się do komunikatów o błędach w powłoce:

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

GNU paralleldziała inaczej. Dzięki temu nie chcesz używać, sh -cponieważ paralleljuż uruchamia powłokę i próbuje zastąpić {}argumentem cytowanym we właściwej składni powłoki .

<list.txt PARALLEL_SHELL=sh parallel 'cmd > {}'
Stéphane Chazelas
źródło
ów drugi shwydaje się być jakiś zastępczy, to działa też jeśli zastąpione _na przykład - bardzo przydatna, jeśli chcesz zadzwonić wewnętrzne bash: find /tmp -name 'fil*' -exec bash -c 'printf "%q\n" "$1"' _ {} \;. Ale czy ktoś wie, gdzie to jest udokumentowane?
Florian Fida,
1
@FlorianFida Pierwszym argumentem powłoki jest $0(zwykle nazwa powłoki. Musisz pominąć ją w tym scenariuszu, aby nie zjadł jednego z twoich normalnych argumentów pozycyjnych. Dokumentacja na -cten temat wspomina.
Etan Reisner
2
@EtanReisner inulm.de/~mascheck/various/find/#embedded
Gilles 'SO- przestań być zły'
1
@ phk, nie ma go argv[0]tutaj, to tylko $0scenariusz. Strona Svena jest tutaj niedokładna, rnie spowoduje, że powłoka wejdzie w tryb ograniczony, o ile jest to możliwe, i zshnie zmieni trybu na podstawie $0. (exec -a rksh ksh -c 'cd /')uruchomi ograniczony ksh, ale nie ksh -c 'cd /' rksh).
Stéphane Chazelas
1
Stéphane, jeśli nie masz nic przeciwko, to czy masz odpowiedź, która wyjaśnia „dlaczego nie osadzić {} w kodzie powłoki”? Poszedłem chociaż wszystkie odpowiedzi pod znalezisku , ale nie mógł znaleźć jeden chociaż mógłbym przysiąc, widziałem kilka rzeczy, które trzeba było napisane na ten temat, ale nie mogę sobie przypomnieć, czy była to odpowiedź, komentarz na czacie lub w innym miejscu jak compunix ... jeśli / kiedy masz czas, czy mogłabyś rozszerzenie na „Nigdy osadzić {}” części lub sądzisz powinniśmy mieć dedykowane Q np „wpływ na bezpieczeństwo korzystania findz -exec sh -ci osadzanie {}w Shellcode” ?
don_crissti