find -exec: thêm đối số giữa {} và +

Oct 21 2020

Tôi muốn sử dụng find để định vị tệp, sau đó sao chép chúng vào một thư mục, vì vậy tôi đã thử:

find . -name '*.png' -exec cp {} /tmp/dest +

Tuy nhiên, điều này không thành công với

find: missing argument to `-exec'

Khi tôi thay thế +bằng một, ;nó hoạt động, nhưng gọi cp cho từng tệp riêng lẻ. Làm cách nào để thêm đối số theo sau (chẳng hạn như thư mục đích) khi sử dụng +biểu mẫu của -exec?

Tất nhiên, trong trường hợp này, tôi có thể khắc phục hạn chế rõ ràng bằng cách sử dụng cp -t(chẳng hạn như được chỉ ra trong bài đăng này trên Stack Overflow , nhưng giải pháp đó dành riêng cho cp. Thay vào đó, tôi có thể đang sử dụng rsync, scp hoặc một số công cụ khác. Có một cách chung để thêm các đối số giữa {}và +trong find -exec?

Trả lời

12 Kusalananda Oct 21 2020 at 19:22

Không, nếu bạn đang sử dụng -exec ... {} +, có thể không có gì giữa {}và +ngoài khoảng trắng. Không có cách nào xung quanh điều đó.

Từ đặc điểm kỹ thuật tiêu chuẩn POSIX của các findlệnh :

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

Phần cuối của biểu thức chính phải được đánh dấu bằng dấu a <semicolon>hoặc bằng a <plus-sign>. Chỉ một <plus-sign>ngay sau đối số chỉ chứa hai ký tự mới {}được chấm câu ở cuối biểu thức chính. Các mục đích sử dụng khác của <plus-sign>nó sẽ không được coi là đặc biệt.

Một giải pháp chung chung hơn có thể là

find ... -exec sh -c 'cp "$@" /tmp/dest' sh {} +

Ở đây, một sh -ctập lệnh nội tuyến được thực thi với hàng loạt đối số từ find. Bên trong tập lệnh nội tuyến, "$@"sẽ là danh sách các đối số được truyền (được trích dẫn riêng lẻ), cho phép chúng ta đặt chúng theo ý muốn trên cpdòng lệnh.

Điều này cho phép chúng tôi sử dụng không phải GNU cp(ví dụ như macOS hoặc các hệ thống BSD khác, nơi không có -ttùy chọn) hoặc bất kỳ tiện ích nào khác mà người ta có thể muốn thêm các đối số khác vào cuối danh sách tên đường dẫn find.

Có liên quan:

  • Hiểu tùy chọn -exec của `find`

Không ai yêu cầu điều này, nhưng dù sao ...

Với mục đích trong một biến, destdir:

destdir=/tmp/dest

find ... -exec sh -c 'destdir=$1; shift; cp "$@" "$destdir"' sh "$destdir" {} +

Lưu ý rằng lệnh destdirgọi trong shell findlà một biến riêng biệt với destdirtrong sh -cscript.

Hoặc, với bash:

destdir=/tmp/dest

find ... -exec bash -c 'cp "${@:2}" "$1"' bash "$destdir" {} +

Đây là "cắt" "$@"danh sách để sắp xếp lại nó một cách thích hợp cho cpdòng lệnh, mà không cần trích xuất $1, tên đường dẫn thư mục đích, thành một biến riêng biệt.