tìm -exec trong tập lệnh bash với mở rộng biến


14

Tôi đang cố chạy một lệnh tương tự như lệnh bên dưới trong tập lệnh bash. Nó nên tìm kiếm thông qua tất cả các thư mục con của $sourcedirvà sao chép tất cả các tệp thuộc một loại nhất định vào cấp gốc $targetdir.

#!/bin/bash

# These are set as arguments to the script, not hard-coded
sourcedir="/path/to/sourcedir"
targetdir="/path/to/targetdir"

find "$sourcedir" -type f -name "*.type" -exec sh -c 'cp "$1" "$2/`basename "$1"`"' "{}" "$targetdir" \;

Điều này có vẻ khá gần, ngoại trừ việc {}không được thông qua tại như $2để-exec sh -c ...

Tôi muốn làm điều này càng gần "đúng cách" càng tốt, với sự khoan dung cho các ký tự đặc biệt trong tên tệp (cụ thể là các ký tự trích dẫn).

Chỉnh sửa: Tôi thấy mọi người đề nghị sử dụng xargshoặc lập luận chuỗi. Tôi đã có ấn tượng rằng điều này chỉ ổn đối với một số lượng đối số hạn chế. Ví dụ, nếu tôi có hàng ngàn tệp .jpg tôi đang cố sao chép từ một số thư mục thư viện vào một thư mục trình chiếu khổng lồ, liệu các giải pháp xâu chuỗi có còn hoạt động không?

Chỉnh sửa 2: Vấn đề của tôi là tôi đã bị thiếu một _trước khi tùy chọn đầu tiên của tôi chuyển sang -execlệnh. Đối với bất kỳ ai tò mò về cách làm cho lệnh find hoạt động, hãy thêm _và mọi thứ sẽ ổn:

find "$sourcedir" -type f -name "*.type" -exec sh -c 'cp "$1" "$2"' _ "{}" "$targetdir" \;

Tôi đã chấp nhận một câu trả lời dưới đây mặc dù nó hoàn thành nhiệm vụ tương tự, nhưng hiệu quả và thanh lịch hơn.


4
Đó chính xác là lý do tại sao xargsđược tạo ra, để tự động xử lý số lượng lớn đối số trên các lệnh thông thường có giới hạn. Cũng để xem xét, hầu hết các giới hạn đối số tối đa, đã được cải thiện rất nhiều cho các tiện ích GNU tiêu chuẩn. Bạn cũng sẽ thấy một lợi ích hiệu suất, tránh tất cả các nhánh quá trình, mà trên hàng ngàn tệp có liên quan.
JM Becker

Với gnu-find và + thay vì ";", bạn có thể xử lý nhiều đối số cùng một lúc với find. Và bạn lưu đối số phức tạp đi qua với -print0.
người dùng không xác định

@userunknown: Tôi đang trả lời câu hỏi này bên dưới câu trả lời của bạn.
JM Becker

@user không rõ, tôi rất thích mã này. Nó ít nhất hoàn toàn tuân thủ POSIX và sẽ hoạt động mà không cần bất kỳ công cụ GNU nào trên máy. Có những lần khi bạn làm cần điều này, đặc biệt là trên các máy chủ tại nơi làm việc.
cú pháp

Câu trả lời:


6

Bạn muốn sao chép các tập tin của một loại cụ thể, vào một thư mục cụ thể? Điều này được thực hiện tốt nhất với xargs, và bạn thậm chí không cần điều đó sh. Đây là một cách thích hợp hơn để làm điều đó, cũng nên chạy hiệu quả hơn.

find "$sourcedir" -type f -name "*.type" | xargs cp -t targetdir

Nếu bạn cần xử lý tên tệp đặc biệt, thì hãy sử dụng NULLlàm người tách biệt của bạn

find "$sourcedir" -type f -name "*.type" -print0 | xargs -0 cp -t "$targetdir"

1
Đối với trường hợp thứ 2, đừng quên thêm -print0vào find-0đểxargs
SiegeX

@ SiegeX, đã làm điều đó, trước khi tôi nhận thấy bình luận của bạn.
JM Becker

Ngoài ra, NULLkhông cần thiết nếu bạn sử dụng '{}', nó rất quan trọng khi bạn không sử dụng. Lợi ích thực sự giữa một trong những người khác, ngoài việc POSIXtuân thủ, là hiệu suất.
JM Becker

6

Bạn cần chuyển {}như một đối số cho shell sau đó lặp qua từng đối số.

find "$sourcedir" -type f -name "*.type" -exec sh -c 'for f; do cp "$f" "$0"; done' "$targetdir" {} +

Lưu ý : Cách thức hoạt động này là đối số đầu tiên với shell là tên của shell , chúng ta có thể khai thác điều này bằng cách chuyển tên dưới dạng $targetdirvà sau đó sử dụng tham số đặc biệt$0 bên trong tập lệnh shell để truy cập vào targetdir đó.


1
"$targetdir"không được mở rộng bên trong dấu ngoặc đơn.
enzotib

5

Nếu bạn không tin vào nhà thờ của xargs:

find "$sourcedir" -type f -name "*.mp3" -exec cp -t "$targetdir" {} +

Giải trình:

cp -t a b c d 

bản sao b, c và d vào thư mục đích a.

-exec cmd {} +

Gọi lệnh trên một loạt các tệp lớn cùng một lúc, không phải lần lượt từng tệp khác (là tiêu chuẩn, nếu bạn sử dụng ";" thay vì +). Đây là lý do tại sao phải kéo targetdir ra phía trước và đánh dấu nó rõ ràng là mục tiêu.

Điều này hoạt động cho gnu-find và có thể không cho các triển khai tìm kiếm khác. Tất nhiên nó cũng dựa vào -t -flag.

TechZilla tất nhiên là rất đúng, như sh không cần thiết phải gọi cp.

Nếu bạn không sử dụng xargs, phần lớn thời gian không cần kết hợp với find, bạn sẽ được lưu từ việc học -print0-0gắn cờ.


1
Obvisouly, phương pháp mà bất cứ ai cũng có thể thích là ai đó chủ quan. Với những gì đã nói, có tồn tại lý do để vẫn sử dụng xargs. Ví dụ lớn nhất, nếu bạn không tìm thấy tệp thì sao? Tôi sử dụng xargs thay cho forcác vòng lặp chung mọi lúc, findphạm vi nhỏ hơn nhiều. Ngoài ra, findchỉ hỗ trợ +nếu bạn đang sử dụng GNU find, nó không được xác định trong POSIX. Vì vậy, trong khi bạn nên cảm thấy thoải mái khi thích findmột mình, Nó không làm mọi thứ xargs làm. Khi bạn xem xét GNU, xargsbạn cũng sẽ nhận được -Pđa lõi. Nó đáng để học xargsbất kể.
JM Becker

Nói chủ quan, ý kiến ​​của tôi rõ ràng là khác nhau. Mặt khác, khách quan của bạn cũng đúng. Đây là một trong số ít các giải pháp tốt nhất hiện có.
JM Becker

@TechZilla: Tôi hy vọng sẽ nhớ truy cập lại trang web này, khi tìm thấy bắt đầu hỗ trợ gọi song song. :) Trong hầu hết các trường hợp sao chép / di chuyển, tốc độ ổ đĩa sẽ là yếu tố hạn chế, nhưng SSD có thể thay đổi hình ảnh. Bạn nói đúng, rằng một giải pháp không phải GNU để cung cấp là một điều tốt. Khác, giải pháp tìm kiếm ngắn hơn khách quan, đơn giản hơn và chỉ sử dụng hai quy trình.
người dùng không xác định

0
read -p "SOURCE: " sourcedir
read -p "TYPE: " type
read -p "TARGET: " targetdir
find -L $sourcedir -iname "*.$type" -exec cp -v {} $targetdir \;

3
Xem xét thêm một số giải thích về giải pháp của bạn.
HalosGhost
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.