Làm cách nào để tìm kiếm thất bại nếu -exec thất bại?


29

Khi tôi chạy lệnh này trong shell (trong một thư mục không trống):

find . -exec invalid_command_here {} \;

Tôi nhận được điều này:

find: invalid_command_here: No such file or directory
find: invalid_command_here: No such file or directory
find: invalid_command_here: No such file or directory

(v.v. cho mỗi tệp)

Tôi cần findphải thất bại sau lỗi đầu tiên. Có cách nào để làm việc này không? Tôi không thể sử dụng xargs, vì tôi có khoảng trắng trong đường dẫn của mình, nhưng tôi cần đoạn script gọi lệnh này để trả về mã lỗi.

Câu trả lời:


34

Đây là một hạn chế của find. Các POSIX tiêu chuẩn quy định cụ thể rằng tình trạng trả lại findlà 0, trừ khi một lỗi xảy ra trong khi vượt qua các thư mục; trạng thái trả về của các lệnh được thực thi không nhập vào nó.

Bạn có thể tạo các lệnh ghi trạng thái của chúng vào một tệp hoặc tới một mô tả:

find_status_file=$(mktemp findstatus)
: >"$find_status_file"
find  -exec sh -c 'trap "echo \$?" EXIT; invalid_command "$0"' {} \;
if [ -s "$find_status_file" ]; then
  echo 1>&2 "An error occurred"
fi
rm -f "$find_status_file"

Một phương pháp khác, như bạn đã khám phá , là sử dụng xargs. Các xargslệnh luôn xử lý tất cả các tệp, nhưng trả về trạng thái 1 nếu bất kỳ lệnh nào trả về trạng thái khác.

find  -print0 | xargs -0 -n1 invalid_command

Tuy nhiên, một phương pháp khác là tránh findvà sử dụng tính năng đệ quy trong vỏ thay thế: **/có nghĩa là bất kỳ độ sâu của các thư mục con. Điều này đòi hỏi phiên bản 4 trở lên của bash; macOS bị kẹt ở phiên bản 3.x, vì vậy bạn phải cài đặt nó từ bộ sưu tập cổng. Sử dụng set -eđể tạm dừng tập lệnh trên lệnh đầu tiên trả về trạng thái khác không.

shopt -s globstar
set -e
for x in **/*.xml; do invalid_command "$x"; done

Xin lưu ý rằng trong bash 4.0 đến 4.2, điều này hoạt động nhưng đi qua các liên kết tượng trưng đến các thư mục, điều này thường không được mong muốn.

Nếu bạn sử dụng zsh thay vì bash, tính năng đệ quy đệ quy sẽ không có vấn đề gì. Zsh có sẵn theo mặc định trên OSX / macOS. Trong zsh, bạn chỉ có thể viết

set -e
for x in **/*.xml; do invalid_command "$x"; done

Cách xargstiếp cận hoạt động nói chung nhưng bằng cách nào đó phá vỡ bash -ccác lệnh. Ví dụ : find . -name '*.xml' -print0 | xargs -0 -n 1 -I '{}' bash -c "foo {}". Điều này được thực hiện nhiều lần trong khi find . -name '2*.xml' -print0 | xargs -0 -n 1 -I '{}' foo {}được thực hiện một lần và thất bại. Bất cứ ý tưởng tại sao?
DKroot

@DKroot Không bao giờ sử dụng {}bên trong bash -c. Cái này lấy tên tập tin và chèn nó trực tiếp vào lệnh shell. Nếu tên tệp chứa các ký tự có ý nghĩa đặc biệt trong shell như dấu cách, shell sẽ diễn giải các ký tự đặc biệt này như vậy. Nếu bạn cần một shell, hãy chuyển {}như một đối số riêng biệt, ví dụ bash -c 'foo "$0"' {}(lưu ý cả hai dấu ngoặc kép xung quanh $0).
Gilles 'SO- ngừng trở nên xấu xa'

OK, trích dẫn câu hỏi sang một bên, tại sao sau đây không dừng lại ở lỗi đầu tiên ?? find . -name '*' -print0 | xargs -0 -n 1 -I '{}' bash -c 'foo "$0"' {}
DKroot

@DKroot Tại sao nó dừng lại ở một lỗi? xargs luôn chạy lệnh trên tất cả các mục.
Gilles 'SO- ngừng trở nên xấu xa'

Tôi đang cố gắng sử dụng câu trả lời này: find . -print0 | xargs -0 -n1 invalid_commandphương pháp xargs ( ). Điều này dừng lại ở lỗi đầu tiên chính xác : find . -name '*' -print0 | xargs -0 -n 1 -I '{}' foo {}. Tuyệt quá! Nhưng cách tiếp cận tương tự không hoạt động với bash -c(ở trên). Sự khác biệt duy nhất giữa hai là bash -c.
DKroot

18

Tôi có thể sử dụng điều này thay thế:

find . -name *.xml -print0 | xargs -n 1 -0 invalid_command

4

xargslà một lựa chọn. Tuy nhiên, thật ra cũng dễ dàng để làm điều này findbằng cách sử dụng +thay vì\;

-exec  utility_name  [argument ...]   {} +

Từ tài liệu POSIX :

Nếu biểu thức chính được đánh dấu bằng dấu cộng, thì biểu thức chính sẽ luôn được đánh giá là đúng và các tên đường dẫn được đánh giá chính sẽ được tổng hợp thành các tập hợp. Utility_name tiện ích sẽ được gọi một lần cho mỗi bộ tên đường dẫn tổng hợp. Mỗi lệnh gọi sẽ bắt đầu sau khi tên đường dẫn cuối cùng trong tập hợp được tổng hợp và sẽ được hoàn thành trước khi thoát khỏi tiện ích tìm kiếm và trước khi tên đường dẫn đầu tiên trong tập tiếp theo (nếu có) được tổng hợp cho lệnh chính này, nhưng nếu không thì sẽ không xác định được liệu lời gọi đó xảy ra trước, trong hoặc sau khi đánh giá các bầu cử sơ bộ khác. Nếu bất kỳ lệnh gọi nào trả về giá trị khác không là trạng thái thoát, tiện ích find sẽ trả về trạng thái thoát khác không.Một đối số chỉ chứa hai ký tự, {{} sẽ được thay thế bằng tập hợp các tên đường dẫn tổng hợp, với mỗi tên đường dẫn được chuyển thành một đối số riêng cho tiện ích được gọi theo cùng thứ tự mà nó được tổng hợp. Kích thước của bất kỳ bộ hai hoặc nhiều tên đường dẫn sẽ bị giới hạn sao cho việc thực thi tiện ích không khiến giới hạn {ARG_MAX} của hệ thống bị vượt quá. Nếu có nhiều hơn một đối số chỉ chứa hai ký tự mà {{} có mặt, thì hành vi đó không được chỉ định.

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.