find + xargs: dòng đối số quá dài


21

Tôi có một dòng như sau:

find /foo/bar -name '*.mp4' -print0 | xargs -i {} -0 mv -t /some/path {}

nhưng tôi đã nhận được lỗi sau:

xargs: argument line too long

Tôi bị bối rối. Không phải là việc sử dụng xargsđược cho là để giúp chính xác vấn đề này sao?

Lưu ý: Tôi biết rằng tôi có thể sử dụng một cách công nghệ -exectrong tìm kiếm, nhưng tôi muốn hiểu tại sao điều trên không thành công, vì sự hiểu biết của tôi xargslà phải biết cách chia đầu vào thành một kích thước có thể quản lý được cho đối số mà nó chạy. Điều này có đúng không?

Đây là tất cả với zsh.

Câu trả lời:


11

Vâng, vì một điều mà công -itắc không được chấp nhận:

-i[replace-str]
     This  option  is a synonym for -Ireplace-str if replace-str is specified. 
     If the replace-str argument is missing, the effect is the same as -I{}. 
     This option is deprecated; use -I instead.

Vì vậy, khi tôi thay đổi lệnh của bạn xung quanh điều này, nó đã hoạt động:

$ find /foo/bar -name '*.mp4' -print0 | xargs -I{} -0 mv -t /some/path {}

Thí dụ

$ find . -print0 | xargs -I{} -0 echo {}
.
./.sshmenu
./The GIT version control system.html
./.vim_SO
./.vim_SO/README.txt
./.vim_SO/.git
./.vim_SO/.git/objects
./.vim_SO/.git/objects/pack
./.vim_SO/.git/objects/pack/pack-42dbf7fe4a9b431a51da817ebf58cf69f5b7117b.idx
./.vim_SO/.git/objects/pack/pack-42dbf7fe4a9b431a51da817ebf58cf69f5b7117b.pack
./.vim_SO/.git/objects/info
./.vim_SO/.git/refs
./.vim_SO/.git/refs/tags
...

Sử dụng -I{}

Cách tiếp cận này không nên được sử dụng kể từ khi chạy cấu trúc lệnh này:

$ find -print0 ... | xargs -I{} -0 ...

ngầm bật các công tắc này sang xargs, -x-L 1. Các -L 1cấu hìnhxargs để nó gọi các lệnh mà bạn muốn nó chạy các tệp thông qua một cách duy nhất.

Vì vậy, điều này đánh bại mục đích sử dụng xargsở đây vì nếu bạn cung cấp cho nó 1000 tệp, nó sẽ chạymv lệnh 1000 lần.

Vậy tôi nên sử dụng phương pháp nào sau đó?

Bạn có thể làm điều đó bằng cách sử dụng xargs như thế này:

$ find /foot/bar/ -name '*.mp4' -print0 | xargs -0 mv -t /some/path

Hoặc chỉ cần tìm làm tất cả:

$ find /foot/bar/ -name '*.mp4' -exec mv -t /some/path {} +

Cảm ơn! Khi bạn nói "This approach shouldn't be used"phương pháp nào nên được sử dụng thay thế sau đó? Sẽ "find /foot/bar/ -name '*.csv' -print0 | xargs -0 mv -t some_dir'"là một giải pháp tốt hơn? Nếu vậy, làm thế nào để xargsbiết trong trường hợp này trong mvlệnh trong nguồn cấp dữ liệu mà nó nhận được từ đường ống? (nó có luôn đặt chúng cuối cùng không?)
Amelio Vazquez-Reina

@ user815423426 - Làm điều đó với chỉ find ... -exec ...là một cách tốt hơn hoặc nếu bạn muốn sử dụng xargscác find ... | xargs ... mv -t ...là tốt quá. Yup nó luôn đặt chúng cuối cùng. Đó là lý do tại sao phương pháp đó cần -t.
slm

5

Tùy chọn -icó một đối số tùy chọn. Vì bạn đặt một khoảng trắng sau -iđó, không có đối số cho -itùy chọn và do đó, -0không có tùy chọn nào sau đó là tùy chọn xargsthứ hai trong 6 toán hạng {} -0 mv -t /some/path {}.

Chỉ với tùy chọn -i, xargs dự kiến ​​một danh sách tên tệp được phân tách bằng dòng mới. Vì có thể không có dòng mới trong đầu vào, xargs đã nhận được cái trông giống như một tên tệp khổng lồ (với các byte rỗng được nhúng, nhưng xargs không kiểm tra điều đó). Chuỗi đơn này chứa toàn bộ đầu ra finddài hơn độ dài dòng lệnh tối đa, do đó dòng lệnh lỗi lỗi quá dài.

Lệnh của bạn sẽ làm việc với -i{}thay vì -i {}. Ngoài ra, bạn có thể đã sử dụng -I {}: -Itương tự -i, nhưng có một đối số bắt buộc, vì vậy đối số tiếp theo được truyền cho xargsđược sử dụng làm đối số của -Itùy chọn. Sau đó, đối số sau -0đó được hiểu là một tùy chọn, v.v.

Tuy nhiên, bạn không nên sử dụng -I {}tất cả. Sử dụng -Icó ba tác dụng:

  • -Itắt xử lý báo giá, mà -0đã làm.
  • -Ithay đổi chuỗi để thay thế, nhưng {}là giá trị mặc định.
  • -Ilàm cho lệnh được thực thi riêng cho từng bản ghi đầu vào, điều này vô dụng ở đây vì lệnh của bạn ( mv -t) được dành riêng để đối phó với nhiều tệp cho mỗi lần gọi.

Hoặc là thả -I-ihoàn toàn

find /foo/bar -name '*.mp4' -print0 | xargs -0 mv -t /some/path {}

hoặc thả xargs và sử dụng -exec:

find /foo/bar -name '*.mp4' -exec mv -t /some/path {} +

0

Hãy thử sử dụng bash for loop:

for FILE in *.mp4 ; do rm $FILE ; done

hoặc nếu bạn muốn xem những gì đang xảy ra:

for FILE in *.mp4 ; do echo Removing $FILE ; rm $FILE ; done

0

Nếu bạn đang nhìn thấy điều này trong khi sử dụng vỏ cá .
Điều này liên quan đến cách cá mở rộng chuỗi thay thế{}

Nếu bạn đang sử dụng cá, bạn cần thoát khỏi chuỗi thay thế \{\}

| xargs -I \{\} echo \{\}

hoặc sử dụng một chuỗi thay thế khác

| xargs -I ! echo !
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.