Câu trả lời này có trong các phần sau:
- Sử dụng cơ bản của
-exec
- Sử dụng
-exec
kết hợp vớish -c
- Sử dụng
-exec ... {} +
- Sử dụng
-execdir
Sử dụng cơ bản của -exec
Các -exec
tùy chọn có một tiện ích bên ngoài với đối số tùy chọn như là đối số và thực thi nó của nó.
Nếu chuỗi {}
có mặt ở bất cứ đâu trong lệnh đã cho, mỗi phiên bản của chuỗi sẽ được thay thế bằng tên đường dẫn hiện đang được xử lý (ví dụ ./some/path/FILENAME
). Trong hầu hết các shell, hai ký tự {}
không cần trích dẫn.
Lệnh cần được kết thúc bằng một ;
for find
để biết nó kết thúc ở đâu (vì có thể có thêm tùy chọn sau đó). Để bảo vệ ;
vỏ khỏi vỏ, nó cần được trích dẫn là \;
hoặc ';'
, nếu không, vỏ sẽ xem nó là phần cuối của find
lệnh.
Ví dụ ( \
ở cuối hai dòng đầu tiên chỉ dành cho tiếp tục dòng):
find . -type f -name '*.txt' \
-exec grep -q 'hello' {} ';' \
-exec cat {} ';'
Điều này sẽ tìm thấy tất cả các tệp thông thường ( -type f
) có tên khớp với mẫu *.txt
trong hoặc bên dưới thư mục hiện tại. Sau đó, nó sẽ kiểm tra xem chuỗi có hello
xảy ra trong bất kỳ tệp nào được tìm thấy hay grep -q
không (không tạo ra bất kỳ đầu ra nào, chỉ là trạng thái thoát). Đối với những tệp có chứa chuỗi, cat
sẽ được thực thi để xuất nội dung của tệp vào thiết bị đầu cuối.
Mỗi cái -exec
cũng hoạt động như một "phép thử" trên các tên đường được tìm thấy find
, giống như -type
và -name
không. Nếu lệnh trả về trạng thái thoát không (biểu thị "thành công"), phần tiếp theo của find
lệnh sẽ được xem xét, nếu không find
lệnh sẽ tiếp tục với tên đường dẫn tiếp theo. Điều này được sử dụng trong ví dụ trên để tìm các tệp có chứa chuỗi hello
, nhưng bỏ qua tất cả các tệp khác.
Ví dụ trên minh họa hai trường hợp sử dụng phổ biến nhất là -exec
:
- Như một thử nghiệm để tiếp tục hạn chế tìm kiếm.
- Để thực hiện một số loại hành động trên tên đường dẫn tìm thấy (thông thường, nhưng không nhất thiết, ở cuối
find
lệnh).
Sử dụng -exec
kết hợp vớish -c
Lệnh -exec
có thể thực thi được giới hạn trong một tiện ích bên ngoài với các đối số tùy chọn. Để sử dụng vỏ tích hợp, chức năng, điều kiện, đường ống, chuyển hướng, vv trực tiếp với -exec
là không thể, trừ khi được bọc trong một cái gì đó như sh -c
vỏ con.
Nếu bash
các tính năng được yêu cầu, sau đó sử dụng bash -c
thay thế sh -c
.
sh -c
chạy /bin/sh
với một tập lệnh được đưa ra trên dòng lệnh, theo sau là các đối số dòng lệnh tùy chọn cho tập lệnh đó.
Một ví dụ đơn giản về việc sử dụng sh -c
chính nó, không có find
:
sh -c 'echo "You gave me $1, thanks!"' sh "apples"
Điều này chuyển hai đối số cho tập lệnh shell con:
Chuỗi sh
. Điều này sẽ có sẵn như $0
bên trong tập lệnh và nếu shell bên trong xuất ra một thông báo lỗi, nó sẽ thêm tiền tố vào chuỗi này.
Đối số apples
có sẵn như $1
trong kịch bản và đã có nhiều đối số hơn, sau đó những đối số này sẽ có sẵn $2
, $3
v.v. Chúng cũng sẽ có sẵn trong danh sách "$@"
(ngoại trừ $0
không có trong đó "$@"
).
Điều này rất hữu ích khi kết hợp với -exec
nó vì nó cho phép chúng ta tạo các tập lệnh phức tạp tùy ý hoạt động trên các tên đường dẫn được tìm thấy bởi find
.
Ví dụ: Tìm tất cả các tệp thông thường có hậu tố tên tệp nhất định và thay đổi hậu tố tên tệp đó thành một số hậu tố khác, trong đó các hậu tố được giữ trong các biến:
from=text # Find files that have names like something.text
to=txt # Change the .text suffix to .txt
find . -type f -name "*.$from" -exec sh -c 'mv "$3" "${3%.$1}.$2"' sh "$from" "$to" {} ';'
Bên trong tập lệnh nội bộ, $1
sẽ là chuỗi text
, $2
sẽ là chuỗi txt
và $3
sẽ là bất kỳ tên đường dẫn find
nào được tìm thấy cho chúng ta. Việc mở rộng tham số ${3%.$1}
sẽ lấy tên đường dẫn và loại bỏ hậu tố .text
khỏi nó.
Hoặc, sử dụng dirname
/ basename
:
find . -type f -name "*.$from" -exec sh -c '
mv "$3" "$(dirname "$3")/$(basename "$3" ".$1").$2"' sh "$from" "$to" {} ';'
hoặc, với các biến được thêm vào trong tập lệnh nội bộ:
find . -type f -name "*.$from" -exec sh -c '
from=$1; to=$2; pathname=$3
mv "$pathname" "$(dirname "$pathname")/$(basename "$pathname" ".$from").$to"' sh "$from" "$to" {} ';'
Lưu ý rằng trong biến thể cuối cùng này, các biến from
và to
trong vỏ con khác biệt với các biến có cùng tên trong tập lệnh bên ngoài.
Trên đây là cách chính xác để gọi một tập lệnh phức tạp tùy ý từ -exec
với find
. Sử dụng find
trong một vòng lặp như
for pathname in $( find ... ); do
là dễ bị lỗi và không phù hợp (ý kiến cá nhân). Nó đang phân tách tên tập tin trên các khoảng trắng, gọi tên tập tin toàn cầu và cũng buộc trình bao mở rộng kết quả hoàn chỉnh find
trước khi chạy vòng lặp đầu tiên của vòng lặp.
Xem thêm:
Sử dụng -exec ... {} +
Các ;
cuối cùng có thể được thay thế bằng +
. Điều này gây ra find
việc thực thi lệnh đã cho với càng nhiều đối số (tên đường dẫn được tìm thấy) càng tốt chứ không phải một lần cho mỗi tên đường dẫn được tìm thấy. Chuỗi {}
phải xảy ra ngay trước khi +
điều này hoạt động .
find . -type f -name '*.txt' \
-exec grep -q 'hello' {} ';' \
-exec cat {} +
Tại đây, find
sẽ thu thập các tên đường dẫn kết quả và thực hiện cat
càng nhiều trong số chúng càng tốt.
find . -type f -name "*.txt" \
-exec grep -q "hello" {} ';' \
-exec mv -t /tmp/files_with_hello/ {} +
Tương tự như vậy ở đây, mv
sẽ được thực hiện ít nhất có thể. Ví dụ cuối cùng này yêu cầu GNU mv
từ coreutils (hỗ trợ -t
tùy chọn).
Sử dụng -exec sh -c ... {} +
cũng là một cách hiệu quả để lặp qua một tập hợp các tên đường dẫn với một tập lệnh phức tạp tùy ý.
Những điều cơ bản giống như khi sử dụng -exec sh -c ... {} ';'
, nhưng tập lệnh hiện có một danh sách các đối số dài hơn nhiều. Chúng có thể được lặp lại bằng cách lặp "$@"
bên trong tập lệnh.
Ví dụ của chúng tôi từ phần cuối thay đổi hậu tố tên tệp:
from=text # Find files that have names like something.text
to=txt # Change the .text suffix to .txt
find . -type f -name "*.$from" -exec sh -c '
from=$1; to=$2
shift 2 # remove the first two arguments from the list
# because in this case these are *not* pathnames
# given to us by find
for pathname do # or: for pathname in "$@"; do
mv "$pathname" "${pathname%.$from}.$to"
done' sh "$from" "$to" {} +
Sử dụng -execdir
Ngoài ra còn có -execdir
(được thực hiện bởi hầu hết find
các biến thể, nhưng không phải là một tùy chọn tiêu chuẩn).
Điều này hoạt động giống như -exec
sự khác biệt mà lệnh shell đã cho được thực thi với thư mục của tên đường dẫn tìm thấy là thư mục làm việc hiện tại của nó và {}
sẽ chứa tên cơ sở của tên đường dẫn tìm thấy mà không có đường dẫn của nó (nhưng GNU find
vẫn sẽ đặt tiền tố cho tên cơ sở ./
trong khi BSD find
sẽ không làm điều đó).
Thí dụ:
find . -type f -name '*.txt' \
-execdir mv {} done-texts/{}.done \;
Điều này sẽ di chuyển từng tệp được tìm thấy vào thư mục con *.txt
tồn tại trong cùng thư mục với nơi tìm thấy tệp . Tập tin cũng sẽ được đổi tên bằng cách thêm hậu tố vào nó.done-texts
.done
Điều này sẽ phức tạp hơn một chút -exec
vì chúng ta sẽ phải lấy tên cơ sở của tệp tìm thấy ra {}
để tạo tên mới của tệp. Chúng tôi cũng cần tên thư mục từ {}
để xác định vị trí done-texts
thư mục đúng.
Với -execdir
, một số điều như thế này trở nên dễ dàng hơn.
Các hoạt động tương ứng bằng cách sử dụng -exec
thay vì -execdir
sẽ phải sử dụng một vỏ con:
find . -type f -name '*.txt' -exec sh -c '
for name do
mv "$name" "$( dirname "$name" )/done-texts/$( basename "$name" ).done"
done' sh {} +
hoặc là,
find . -type f -name '*.txt' -exec sh -c '
for name do
mv "$name" "${name%/*}/done-texts/${name##*/}.done"
done' sh {} +