Tại sao tìm -exec mv {} ./target/ + không hoạt động?


98

Tôi muốn biết chính xác những gì {} \;{} \+| xargs ...làm. Hãy làm rõ những điều này với những lời giải thích.

Dưới đây 3 lệnh chạy và cho ra cùng một kết quả nhưng lệnh đầu tiên mất một chút thời gian và định dạng cũng khác một chút.

find . -type f -exec file {} \;
find . -type f -exec file {} \+
find . -type f | xargs file

Đó là bởi vì cái đầu tiên chạy filelệnh cho mọi tệp đến từ findlệnh. Vì vậy, về cơ bản nó chạy như sau:

file file1.txt
file file2.txt

Nhưng sau đó 2 tìm bằng -execlệnh chạy tệp lệnh một lần cho tất cả các tệp như dưới đây:

file file1.txt file2.txt

Sau đó, tôi chạy các lệnh sau, lệnh đầu tiên chạy mà không có vấn đề gì nhưng lệnh thứ hai đưa ra thông báo lỗi.

find . -type f -iname '*.cpp' -exec mv {} ./test/ \;
find . -type f -iname '*.cpp' -exec mv {} ./test/ \+ #gives error:find: missing argument to `-exec'

Đối với lệnh với {} \+, nó cho tôi thông báo lỗi

find: missing argument to `-exec'

tại sao vậy? bất cứ ai có thể xin vui lòng giải thích những gì tôi đang làm sai?


câu hỏi thực sự rất đơn giản, tại sao cái đầu tiên hoạt động và cái thứ hai thì không? (1) tìm. -type f -iname ' .cpp' -exec mv {} ./test/ \; (2) tìm. -type f -iname ' .cpp' -exec mv {} ./test/ \ +
Shahadat Hossain

Câu trả lời:


185

Các trang hướng dẫn (hoặc GNU thủ công trực tuyến ) khá nhiều giải thích tất cả mọi thứ.

lệnh find -exec {} \;

Đối với mỗi kết quả, command {}được thực thi. Tất cả các lần xuất hiện {}đều được thay thế bằng tên tệp. ;được đặt trước bằng một dấu gạch chéo để ngăn trình bao thông dịch nó.

lệnh find -exec {} +

Mỗi kết quả được nối vào commandvà thực thi sau đó. Có tính đến các giới hạn về độ dài lệnh, tôi đoán rằng lệnh này có thể được thực thi nhiều lần hơn, với trang hướng dẫn hỗ trợ tôi:

tổng số lần gọi lệnh sẽ ít hơn nhiều so với số tệp phù hợp.

Lưu ý trích dẫn này từ trang hướng dẫn sử dụng:

Dòng lệnh được xây dựng giống như cách mà xargs xây dựng các dòng lệnh của nó

Đó là lý do tại sao không có ký tự nào được phép giữa {}+ngoại trừ khoảng trắng. +làm cho find phát hiện rằng các đối số nên được thêm vào lệnh giống như xargs.

Giải pháp

May mắn thay, việc triển khai GNU mvcó thể chấp nhận thư mục đích làm đối số, với một trong hai -thoặc tham số dài hơn --target. Cách sử dụng sẽ là:

mv -t target file1 file2 ...

findLệnh của bạn trở thành:

find . -type f -iname '*.cpp' -exec mv -t ./test/ {} \+

Từ trang hướng dẫn sử dụng:

-exec lệnh;

Thực thi lệnh; true nếu trạng thái 0 được trả về. Tất cả các đối số sau để tìm được coi là đối số của lệnh cho đến khi đối số bao gồm `; ' đang gặp phải. Chuỗi `{} 'được thay thế bằng tên tệp hiện tại đang được xử lý ở mọi nơi mà nó xuất hiện trong các đối số của lệnh, không chỉ trong các đối số chỉ có nó, như trong một số phiên bản của find. Cả hai cấu trúc này có thể cần phải được thoát (bằng dấu `\ ') hoặc được trích dẫn để bảo vệ chúng khỏi bị mở rộng bởi shell. Xem phần VÍ DỤ để biết các ví dụ về việc sử dụng tùy chọn -exec. Lệnh đã chỉ định được chạy một lần cho mỗi tệp phù hợp. Lệnh được thực hiện trong thư mục bắt đầu. Có những vấn đề bảo mật không thể tránh khỏi xung quanh việc sử dụng hành động -exec; bạn nên sử dụng tùy chọn -execdir để thay thế.

-exec lệnh {} +

Biến thể này của hành động -exec chạy lệnh được chỉ định trên các tệp đã chọn, nhưng dòng lệnh được tạo bằng cách thêm mỗi tên tệp đã chọn vào cuối; tổng số lần gọi lệnh sẽ ít hơn nhiều so với số tệp phù hợp. Dòng lệnh được xây dựng giống như cách mà xargs xây dựng các dòng lệnh của nó. Chỉ một phiên bản của `{} 'được phép trong lệnh. Lệnh được thực hiện trong thư mục bắt đầu.


1
Tôi thực sự biết nó hoạt động như thế nào, tôi đã xem qua hướng dẫn này vài lần, nhưng tôi đã gặp thông báo lỗi khi sử dụng {} +, mặc dù hoạt động đối với {} \; và tôi đang sử dụng Cygwin trong windows.
Shahadat Hossain

1
@Shahadat: bạn đã đọc phần trước "Từ trang hướng dẫn sử dụng" chưa? Bạn đã đặt ./test/giữa {}+, nhưng không có ký tự không có khoảng trắng nào được phép giữa các ký tự này.
Lekensteyn

ru nói rằng tôi không nên đặt ./test/ ở giữa {} và +. Sau đó lệnh mv sẽ hoạt động như thế nào; mv cần nguồn là {} và cần đích là ./test/ và kết thúc bằng +. bạn có thể vui lòng viết lệnh những gì bạn nghĩ đúng không?
Shahadat Hossain

@Shahadat: Tôi thấy những gì bạn đang cố gắng đạt được. Windows chạy chương trình chậm, vì vậy bạn muốn kết hợp nó thành một lệnh. Tôi sẽ thêm một thay thế cho câu trả lời.
Lekensteyn

1
Các +lệnh là một chút lạ AFAIU vì nó dính các tập tin tại "the end" (chứ không phải ở vị trí của {}) vậy tại sao sử dụng {}ở tất cả - điều này là khó hiểu. Cảm ơn vì -ttùy chọn mà tôi không biết, có vẻ như tùy chọn đó đã được tạo ra như một giải pháp cho -exec +vấn đề đó !
e2-e4

6

Tôi gặp phải vấn đề tương tự trên Mac OSX , sử dụng trình bao ZSH : trong trường hợp này không có -ttùy chọn cho mv, vì vậy tôi phải tìm giải pháp khác. Tuy nhiên, lệnh sau đã thành công:

find .* * -maxdepth 0 -not -path '.git' -not -path '.backup' -exec mv '{}' .backup \;

Bí quyết được báo giá niềng răng . Không cần dấu ngoặc nhọn ở cuối execlệnh.

Tôi đã thử nghiệm trong Ubuntu 14.04 (với BASHZSH shell), nó hoạt động giống nhau.

Tuy nhiên, khi sử dụng +dấu hiệu, có vẻ như nó thực sự phải ở cuối execlệnh.


{}cần được trích dẫn trong fishand rcshell, nhưng không phải trong zsh, bashcũng không phải bất kỳ shell nào khác của họ Bourne hoặc csh.
Stephane Chazelas

@StephaneChazelas Yep, được kiểm tra lại trong Ubuntu với bash, thực sự các trích dẫn không cần thiết. Thật kỳ lạ, tôi đã gặp sự cố nếu không trích dẫn chúng trong MacOS (đang sử dụng zsh). Nhưng tôi không có một máy Mac ở tầm để thử lại ...
arvymetal

3

Tương đương tiêu chuẩn find -iname ... -exec mv -t dest {} +cho findviệc triển khai không hỗ trợ -inamehoặc mvtriển khai mà không làm hỗ trợ -tlà sử dụng một lớp vỏ để sắp xếp lại các đối số:

find . -name '*.[cC][pP][pP]' -type f -exec sh -c '
  exec mv "$@" /dest/dir/' sh {} +

Bằng cách sử dụng -name '*.[cC][pP][pP]', chúng tôi cũng tránh việc phụ thuộc vào ngôn ngữ hiện tại để quyết định đâu là phiên bản viết hoa của choặc p.

Lưu ý rằng +, trái với ;không phải là đặc biệt trong bất kỳ trình bao nào vì vậy không cần phải trích dẫn (mặc dù trích dẫn sẽ không gây hại, ngoại trừ tất nhiên với các trình bao như rcvậy không hỗ trợ \như một toán tử trích dẫn).

Các dấu /trong /dest/dir/là để mvthất bại với một lỗi thay vì đổi tên foo.cppđể /dest/dirtrong trường hợp chỉ có một cpptập tin đã được tìm thấy và /dest/dirđã không tồn tại hoặc không phải là một thư mục (hoặc liên kết tượng trưng đến thư mục).


Hoạt động +1 ... trong trình bao như là một bước khởi đầu để thực hiện một lệnh thực sự hữu ích cho nhiều trường hợp sử dụng ... tốt đẹp.
Cbhihe

0
find . -name "*.mp3" -exec mv --target-directory=/home/d0k/Музика/ {} \+

Vui lòng thêm một số giải thích cho câu trả lời của bạn để những người khác có thể học hỏi từ nó
Nico Haase

Bạn cần trả lời câu hỏi yêu cầu giải thích. Chỉ mã không phải là câu trả lời.
Lajos Arpad

-1

không, sự khác biệt giữa +\;nên được đảo ngược. +nối các tệp vào cuối lệnh thi sau đó chạy lệnh thực thi và \;chạy lệnh cho mỗi tệp.

Là vấn đề find . -type f -iname '*.cpp' -exec mv {} ./test/ \+nên find . -type f -iname '*.cpp' -exec mv {} ./test/ + không cần phải thoát khỏi nó hoặc chấm dứt+

xargs tôi đã không sử dụng trong một thời gian dài nhưng tôi nghĩ hoạt động như +.


Tôi cũng đã thử với điều này nhưng nhận được thông báo lỗi tương tự. Hơn nữa, ở mọi nơi tôi thấy chỉ sử dụng + nhưng trong cygwin của tôi, tôi phải sử dụng \ + hoặc "+" để hoạt động.
Shahadat Hossain

oh đây là một môi trường cygwin. Xin lỗi, tôi không biết, tôi không sử dụng cygwin shell, tôi chỉ sử dụng * nix.
Mike Ramirez

1
@Shahadat Hossain thử -name "*.cpp"Tôi hầu như không sử dụng -iname trừ khi tôi muốn làm một số tìm kiếm regex khó khăn, như -iname '??? công việc * \ cpp..'
Mike Ramirez

1
@Mike: Tôi nghĩ bạn hiểu sai sự khác biệt giữa -iname-name. -inamelà phiên bản không phân biệt chữ hoa chữ thường -namevà không có sự khác biệt trong việc xử lý các biểu thức chính quy. Tôi khuyên bạn nên thử các lệnh trước khi đăng, lệnh của bạn cũng không thành công trong trình bao của tôi.
Lekensteyn

1
@Lekensteyn Nó đã được thiết lập đó là trường hợp trước khi nhận xét của bạn. Tôi nghĩ rằng tôi đã xác nhận Shahadat trước khi đăng bài của bạn, đó là một "ok" đơn giản. Không, tôi không chạy nó theo cách thủ công, tôi đã làm nó từ đỉnh đầu của mình và hiếm khi sử dụng hình thức tìm kiếm regex đó với find. Nó chỉ là một loại 'có thể giúp đỡ'.
Mike Ramirez
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.