Có thể sử dụng `find -exec sh -c` một cách an toàn không?


29

Tôi đang cố gắng sử dụng findđể echo 0vào một số tệp, nhưng rõ ràng điều này chỉ hoạt động với sh -c:

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

Nhưng sử dụng sh -cvới find -execlàm cho tôi cảm thấy rất khó chịu vì tôi nghi ngờ trích dẫn vấn đề. Tôi loay hoay một chút với nó và rõ ràng sự nghi ngờ của tôi đã được chứng minh:

  • Thiết lập thử nghiệm của tôi:

    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
    
  • Sử dụng find -execsh -cdường như không hoạt động mà không có vấn đề - không cần trích dẫn ở đây:

    martin@dogmeat ~ % find findtest -type f -exec cat {} \;
    one
    two
    three
    
  • Nhưng khi tôi đang sử dụng sh -c {}dường như yêu cầu một số loại trích dẫn:

    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
    
  • Dấu ngoặc kép hoạt động miễn là không có tên tệp nào chứa dấu ngoặc kép:

    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
    
  • Dấu ngoặc đơn hoạt động miễn là không có tên tệp nào chứa dấu ngoặc đơn:

    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
    

Tôi đã không tìm thấy một giải pháp hoạt động trong mọi trường hợp. Có cái gì tôi đang nhìn, hoặc được sử dụng sh -ctrong find -execvốn đã không an toàn?

Câu trả lời:


40

Không bao giờ nhúng {}vào mã shell! Điều đó tạo ra một lỗ hổng tiêm lệnh. Lưu ý rằng đối cat "{}", nó không chỉ về "nhân vật, \, `, $cũng là một vấn đề (xem xét ví dụ một tập tin gọi là ./$(reboot)/accept_ra).

(nhân tiện, một số findtriển khai sẽ không cho phép bạn làm điều đó và POSIX để lại hành vi không xác định khi {}không phải là chính nó trong một cuộc tranh luận find)

Tại đây, bạn muốn chuyển tên tệp dưới dạng đối số riêng biệt sh(không phải trong đối số ) và shtập lệnh nội tuyến ( đối số ) để tham chiếu đến chúng bằng các tham số vị trí:

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

Hoặc, để tránh chạy một shtệp trên mỗi tệp:

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

Điều tương tự cũng áp dụng cho xargs -I{}hoặc zsh's zargs -I{}. Đừng viết:

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

Đó sẽ là một lỗ hổng tiêm lệnh giống như findtrên, nhưng:

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

Điều này cũng có lợi ích là tránh chạy một shtệp cho mỗi tệp và lỗi khi list.txtkhông chứa bất kỳ tệp nào.

Với zsh's zargs, bạn có thể muốn sử dụng một chức năng chứ không phải là cách gọi sh -c:

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

Lưu ý rằng trong tất cả các ví dụ ở trên, phần hai shở trên đi vào tập lệnh nội tuyến $0. Bạn nên sử dụng một cái gì đó có liên quan có (như shhay find-sh), chứ không phải những thứ thích _, -, --hoặc chuỗi rỗng, như giá trị trong $0được sử dụng cho các thông báo lỗi của shell:

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

GNU parallelhoạt động khác nhau. Với nó, bạn không muốn sử dụng sh -cnhư đã parallelchạy shell và cố gắng thay thế {}bằng đối số được trích dẫn theo cú pháp đúng cho shell .

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

Đó là thứ hai shdường như là một loại giữ chỗ, nó cũng hoạt động nếu được thay thế bằng _ví dụ - rất hữu ích nếu bạn muốn gọi bash internals : find /tmp -name 'fil*' -exec bash -c 'printf "%q\n" "$1"' _ {} \;. Nhưng có ai biết nơi này được ghi nhận?
Florian Fida

1
@FlorianFida Đối số đầu tiên của shell trở thành $0(thường là tên của shell. Bạn cần bỏ qua nó trong kịch bản này để nó không ăn một trong các đối số vị trí bình thường của bạn. Tài liệu cho -cđề cập đến điều này.
Etan Reisner


1
@phk, không có argv[0]ở đây, đó chỉ $0là kịch bản. Trang của Sven không chính xác ở đây, rsẽ không làm cho trình bao vào chế độ hạn chế như có thể nói và zshsẽ không thay đổi chế độ dựa trên $0. (exec -a rksh ksh -c 'cd /')sẽ chạy hạn chế ksh, nhưng không ksh -c 'cd /' rksh).
Stéphane Chazelas

1
Stéphane, nếu bạn không phiền, có câu trả lời nào của bạn giải thích "tại sao không nhúng {} vào mã shell" không? Tôi đã đi qua tất cả các câu trả lời của bạn được tìm thấy nhưng không thể tìm thấy mặc dù tôi có thể thề rằng tôi đã thấy một số nội dung mà bạn đã viết về chủ đề này nhưng không thể nhớ lại nếu đó là một câu trả lời, một nhận xét trong trò chuyện hoặc trên một trang web khác như compunix ... Nếu / khi bạn có thời gian, bạn sẽ nhớ mở rộng trên "không bao giờ nhúng {}" một phần hay bạn nghĩ chúng ta nên có một chuyên Q ví dụ như "ảnh hưởng an ninh của việc sử dụng findvới -exec sh -cvà nhúng {}trong mã shell" ?
don_crissti
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.