bash - tôi có thể làm gì không: tìm thấy -xec cái này && cái đó?


23

Có cách nào để kết hợp một cách hợp lý hai lệnh shell được gọi với find - exec không?

Ví dụ, để in ra tất cả các tệp .csv có chứa chuỗi foo cùng với sự xuất hiện của nó, tôi muốn làm:

find . -iname \*.csv -exec grep foo {} && echo {} \;

nhưng bash phàn nàn với "đối số bị thiếu đối với '-exec'"


2
Bạn có thể sử dụng 2 -exectheo thứ tự hoặc sử dụng một -exec sh -c 'grep foo "$0" && printf %s\\n "$0"' {} \;.
jw013

Điều này đã làm tôi vấp ngã nhiều lần: Tôi luôn mong đợi rằng đối số đầu tiên được chuyển đến sh(trong trường hợp này {}) sẽ $1$0sẽ giống như thế sh. Nhưng trên thực tế, bạn đã đúng, đối số đầu tiên hiển thị là $0. Có đối số đầu tiên là tên của lệnh gọi chỉ là một quy ước, điều đó không được thực thi tự động trong những trường hợp này.
dubiousjim

Có lẽ nên được hợp nhất với câu hỏi này: unix.stackexchange.com/q/18077/4801
dubiousjim

Câu trả lời:


24

-execlà một vị từ chạy một lệnh (không phải là shell) và đánh giá là đúng hoặc sai dựa trên kết quả của lệnh (trạng thái thoát không hoặc không bằng 0).

Vì thế:

find . -iname '*.csv' -exec grep foo {} \; -print

sẽ in đường dẫn tệp nếu greptìm thấy foo trong tệp. Thay vì -printbạn có thể sử dụng một -execvị từ khác hoặc bất kỳ vị từ nào khác

find . -iname '*.csv' -exec grep foo {} \; -exec echo {} \;

Xem thêm !-otìm toán tử cho phủ định và hoặc .

Ngoài ra, bạn có thể bắt đầu một vỏ như:

find . -iname '*.csv' -exec sh -c '
   grep foo "$1" && echo "$1"' sh {} \;

Hoặc để tránh phải khởi động shell cho mọi tệp:

find . -iname '*.csv' -exec sh -c '
  for i do
    grep foo "$i" && echo "$i"
  done' sh {} +

10

Vấn đề bạn gặp phải là trước tiên trình phân tích cú pháp dòng lệnh và thấy hai lệnh đơn giản được phân tách bởi &&toán tử: find . -iname \*.csv -exec grep foo {}echo {} \;. Trích dẫn &&( find . -iname \*.csv -exec grep foo {} '&&' echo {} \;) bỏ qua đó, nhưng bây giờ các lệnh được thực hiện bởi findmột cái gì đó giống như grepvới các đối số foo, wibble.csv, &&, echowibble.csv. Bạn cần hướng dẫn findđể chạy shell sẽ giải thích &&toán tử:

find . -iname \*.csv -exec sh -c 'grep foo "$0" && echo "$0"' {} \;

Lưu ý rằng đối số đầu tiên sau sh -c SOMECOMMAND$0, không $1.

Bạn có thể tiết kiệm thời gian khởi động của một tiến trình shell cho mọi tệp bằng cách nhóm các lệnh yêu cầu với -exec … +. Để dễ xử lý, hãy chuyển một số giá trị giả $0để "$@"liệt kê tên tệp.

find . -iname \*.csv -exec sh -c 'for x in "$@"; do grep foo "$x" && echo "$x"; done' \ {} +

Nếu lệnh shell chỉ là hai chương trình cách nhau &&, findcó thể tự thực hiện công việc: viết hai -exechành động liên tiếp và hành động thứ hai sẽ chỉ được thực hiện nếu chương trình đầu tiên thoát với trạng thái 0.

find . -iname \*.csv -exec grep foo {} \; -exec echo {} \;

(Tôi cho rằng grepechochỉ nhằm mục đích minh họa, vì -exec echocó thể được thay thế -printvà kết quả đầu ra không đặc biệt hữu ích.)


2
Tôi có xu hướng tránh sử dụng "$ 0" cho điều đó, vì nó cũng được sử dụng bởi shell để hiển thị các thông báo lỗi. Ví dụ, bạn có thể thấy một ./some-file: grep: command not foundthông báo lỗi khó hiểu . -exec find sh -c '... "$1"' sh {} \;sẽ không có vấn đề. Có một lỗi đánh máy (liên quan) trong lệnh tìm thứ hai của bạn.
Stéphane Chazelas

3

Trong trường hợp cụ thể này, tôi sẽ làm:

find . -iname \*.csv -exec grep -l foo \{\} \;

Hoặc nếu bạn có ack :

ack -al -G '.*\.csv' foo

Để trả lời câu hỏi thực tế của bạn, một cái gì đó như thế này có thể hoạt động:

find . -iname \*.csv -exec sh -c "grep foo {} && echo {}" \;


3
Lệnh find sau này không chỉ không di động mà còn rất nguy hiểm vì đường dẫn tệp cuối cùng được đánh giá là mã shell.
Stéphane Chazelas

Thêm tất cả lý do để thử và tránh nó bằng cách sử dụng ack / grep đúng cách :)
Dennis Kaarsemaker

1
phương án của jw013 (được đưa ra trong một nhận xét cho câu hỏi) sẽ an toàn hơn về mặt này. (Chỉ cần lưu ý rằng câu trả lời của Gilles và Stephane sử dụng cùng một kỹ thuật.)
dubiousjim
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.