tìm exec '{}' không khả dụng sau>


8

Exec cho phép chúng ta vượt qua tất cả các đối số cùng một lúc {} +hoặc vượt qua từng đối số một{} \;

Bây giờ hãy nói rằng tôi muốn đổi tên tất cả jpeg , không có vấn đề gì khi làm điều này:

find . \( -name '*.jpg' -o -name '*.jpeg' \) -exec mv '{}' '{}'.new \;

Nhưng nếu tôi cần chuyển hướng đầu ra, '{}'không thể truy cập sau khi chuyển hướng.

find . \( -name '*.jpg' -o -name '*.jpeg' \) -exec cjpeg -quality 80 '{}' > optimized_'{}' \;

Điều này sẽ không làm việc. Tôi phải sử dụng một vòng lặp for, lưu trữ đầu ra của find thành một biến trước khi sử dụng nó. Hãy thừa nhận nó, nó cồng kềnh.

for f in `find . \( -name '*.jpg' -o -name '*.jpeg' \)`; do cjpeg -quality 80 $f > optimized_$f; done;

Vậy có cách nào tốt hơn không?


Không có >thiếu trong mẫu mã thứ ba?
choroba

1
Ngay cả dòng đầu tiên của bạn là không chuẩn và do đó không thể mang theo. Cố gắng tránh các dòng lệnh {}xuất hiện trong một chuỗi dài hơn vì các chuỗi như vậy thường không được mở rộng.
schily

Đó là ví dụ đầu tiên không làm những gì bạn nói.
ctrl-alt-delor

1
Bạn đã sửa câu hỏi của mình: Tôi sẽ không còn thay đổi nó nữa.
ctrl-alt-delor

1
Vì giá trị của nó, lý do chính khiến việc chuyển hướng của bạn không hoạt động như bạn muốn là nó được xử lý bởi trình bao mà bạn khởi chạy find, một lần và áp dụng cho findchính lệnh đó. Không {}có ý nghĩa đặc biệt trong bối cảnh đó. Chuyển hướng không phải là một đối số findvà chắc chắn nó không phải là một phần của -execmệnh đề.
John Bollinger

Câu trả lời:


13

Bạn có thể sử dụng bash -ctrong find -execlệnh và sử dụng tham số vị trí với lệnh bash:

find . \( -name '*.jpg' -o -name '*.jpeg' \) -exec bash -c 'cjpeg -quality 80 "$1" > "$(dirname "$1")/optimized_$(basename "$1")"' sh {} \;

Đó là cách {}được cung cấp với $1.

Các shtrước khi {}nói với vỏ bên trong "cái tên" của nó, chuỗi được sử dụng ở đây được sử dụng trong ví dụ thông báo lỗi. Điều này được thảo luận nhiều hơn trong câu trả lời này trên stackoverflow .


8

Bạn có câu trả lời ( https://unix.stackexchange.com/a/481687/4778 ), nhưng đây là lý do tại sao.

Việc chuyển hướng >, và cả các đường ống |$mở rộng đều được thực hiện bởi trình bao trước khi lệnh được thực thi. Do đó, thiết bị xuất chuẩn được chuyển hướng đến optimized_{}, trước khi findbắt đầu.


3

Chuyển hướng cần phải được trích dẫn để tránh rằng shell hiện tại diễn giải nó.
Nhưng trích dẫn nó cũng sẽ tránh đầu ra của lệnh được chuyển hướng.
Giải pháp đã biết cho vấn đề này là gọi shell:

find . -name '*.jpg' -exec sh -c 'echo "$1" >"$1".new' called_shell '{}' \;

Trong trường hợp này, chuyển hướng ( >) được trích dẫn trên shell hiện tại và hoạt động chính xác bên trong shell được gọi. Nó called_shellđược sử dụng làm $0tham số (tên) của shell con ( sh).

Điều đó hoạt động tốt nếu một hậu tố được thêm tên của tệp, nhưng không phải nếu bạn sử dụng tiền tố. Để một tiền tố hoạt động, bạn cần cả hai để loại bỏ phần ./tìm kiếm trước tên tệp ${1#./}và sử dụng -execdirtùy chọn.

Bạn có thể (hoặc không thể) muốn sử dụng các -inametùy chọn để tập tin có tên *.JPGhoặc *.JpGhoặc các biến thể khác cũng được bao gồm.

find . \( -iname '*.jpg' -o -iname '*.jpeg' \) -execdir sh -c '
     cjpeg -quality 80 "$1" > optimized_"${1#./}"
     ' called_shell '{}' \;

Và, bạn có thể (hoặc có thể không) cũng muốn gọi shell một lần cho mỗi thư mục thay vì một lần cho mỗi tệp bằng cách thêm một vòng lặp ( for f do … ; done) và một +ở cuối:

find . \( -iname '*.jpg' -o -iname '*.jpeg' \) -execdir sh -c '
     for f; do cjpeg -quality 80 "$f" > optimized_"${f#./}"; done
     ' called_shell '{}' \+

Và cuối cùng, khi cjpegcó thể ghi trực tiếp vào một tệp, việc chuyển hướng có thể tránh được như sau:

find . \( -iname '*.jpg' -o -iname '*.jpeg' \) -execdir sh -c '
     for f; do cjpeg -quality 80 "$f" -outfile optimized_"${f#./}"; done
     ' called_shell '{}' \+

3

cjpegcó một tùy chọn cho phép bạn ghi vào một tệp có tên, thay vì đầu ra tiêu chuẩn. Nếu phiên bản findhỗ trợ của bạn -execdirtùy chọn, bạn có thể tận dụng điều đó để làm cho việc chuyển hướng không cần thiết.

find . \( -name '*.jpg' -o -name '*.jpeg' \) \
  -execdir cjpeg -quality 80 -outfile optimized_'{}' '{}' \;

Lưu ý: điều này thực sự giả định phiên bản BSD của find, xuất hiện để loại bỏ phần dẫn ./từ tên tệp khi thoát ra {}. (Hoặc ngược lại, GNU find thêm ./ vào tên. Không có tiêu chuẩn nào để nói hành vi nào là "đúng".)


Nếu findhỗ trợ của bạn -execdir, bạn có thể sử dụng đó thay vì -exec. Nó làm cho lệnh chạy trong thư mục nơi tìm thấy tệp và {}sẽ trở thành aa.jpgthay vì ./t2/aa.jpg.
chepner

Vẫn còn lỗi : cjpeg: can't open optimized_./aa.jpg.
Isaac

Hừm, dường như là một sự khác biệt giữa GNU findvà BSD find. (Những nguy hiểm của việc sử dụng các tiện ích mở rộng không chuẩn.)
chepner 14/11/18

Một tên tệp mà không có hàng đầu ./dường như dễ bị lỗi hơn. Một giải pháp hợp lý được đề xuất trong câu trả lời này .
Isaac

1

Tạo tập lệnh cjq80:

#!/bin/bash
cjpeg -quality 80 "$1" > "${1%/*}"/optimized_"${1##*/}"

Làm cho nó thực thi

chmod u+x cjq80

Và sử dụng nó trong -exec:

find . \( -name '*.jpg' -o -name '*.jpeg' \) -exec cjq80 '{}' \;

Tôi đã có mặc dù về điều này, nhưng nó không phải là rất tiện dụng. Đặc biệt là tôi đã phải kết hợp điều này trong quá trình xây dựng
Buzut

Chỉ cần thêm tập lệnh vào các tập lệnh xây dựng khác, tôi thấy nó dễ đọc hơn bash -c gấp đôi.
choroba

Vâng, đó là một vấn đề của hương vị. Bây giờ mọi tùy chọn đều được chỉ định, vì vậy nếu ai đó gặp phải vấn đề tương tự, anh ta sẽ chọn cho mình :)
Buzut 14/11/18
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.