Không xvs xreps làm gì?


25

Tôi biết các greplệnh và tôi đang tìm hiểu về các chức năng của xargs, vì vậy tôi đọc qua này trang đó cung cấp cho một số ví dụ về cách sử dụng các xargslệnh.

Tôi bối rối bởi ví dụ cuối cùng, ví dụ 10. Nó nói "Lệnh xargs thực thi lệnh grep để tìm tất cả các tệp (trong số các tệp được cung cấp bởi lệnh find) có chứa một chuỗi 'stdlib.h'"

$ find . -name '*.c' | xargs grep 'stdlib.h'
./tgsthreads.c:#include
./valgrind.c:#include
./direntry.c:#include
./xvirus.c:#include
./temp.c:#include
...
...
...

Tuy nhiên, sự khác biệt để sử dụng đơn giản là gì

$ find . -name '*.c' | grep 'stdlib.h'

?

Rõ ràng, tôi vẫn đang vật lộn với chính xác những gì xargs đang làm, vì vậy bất kỳ trợ giúp nào đều được đánh giá cao!


2
Câu hỏi này có thể hữu ích: Khi nào cần XArss?
TheOdd

Câu trả lời:


30
$ find . -name '*.c' | grep 'stdlib.h'

Điều này dẫn đầu ra (stdout) * từ findđến (stdin of) * grep 'stdlib.h' dưới dạng văn bản (tức là tên tệp được coi là văn bản). grepthực hiện điều thông thường của nó và tìm các dòng khớp trong văn bản này (bất kỳ tên tệp nào có chứa mẫu). Nội dung của các tập tin không bao giờ được đọc.

$ find . -name '*.c' | xargs grep 'stdlib.h'

Điều này xây dựng một lệnh grep 'stdlib.h' mà mỗi kết quả từ đó findlà một đối số - vì vậy điều này sẽ tìm kiếm các kết quả khớp bên trong mỗi tệp được tìm thấy find( xargscó thể được coi là biến stdin của nó thành đối số cho các lệnh đã cho) *

Sử dụng -type ftrong lệnh find của bạn, hoặc bạn sẽ nhận được lỗi từ grepcác thư mục phù hợp. Ngoài ra, nếu tên tệp có khoảng trắng, xargssẽ bị hỏng nặng, vì vậy hãy sử dụng dấu tách null bằng cách thêm -print0xargs -0để có kết quả đáng tin cậy hơn:

find . -type f -name '*.c' -print0 | xargs -0 grep 'stdlib.h'

* đã thêm các điểm giải thích bổ sung này theo đề xuất trong nhận xét của @cat


2
bạn có thể xem xét ghi nhận (vì có vẻ như bạn bỏ qua) điểm quan trọng mà |ống stdout để grep của stdin mà không giống như các đối số grep và cho kết quả khó hiểu.
con mèo

1
Hoặc sử dụng GNU find's find -name '*.c' -exec grep stdlib.h {} +. Tôi khá nhiều không bao giờ thực sự sử dụng xargs. Cũng ngạc nhiên không ai đề cập rằng xargs phục vụ một mục đích tương tự như grep $(find)thay thế lệnh, vì vậy tôi đã viết một câu trả lời của riêng tôi. Giải thích xargs là thay thế lệnh với ít hạn chế hơn và các vấn đề có vẻ tự nhiên.
Peter Cordes

Một tình huống tôi sử dụng xargs là nếu tôi xóa rất nhiều tệp do kết quả tìm kiếm. Nếu bạn chỉ làm -exec rm, nó sẽ chạy rm trên mỗi tệp một lần, điều này rất không hiệu quả. Đường ống đến xargs sẽ làm tất cả cùng một lúc với một rm. Giới hạn với say -n50 (thực hiện 50 lần) có thể ngăn tràn dòng lệnh (vấn đề với rất nhiều tệp).
lsd

1
@lsd: Tại sao không find -deletecho trường hợp đặc biệt đó? Hoặc đối với các lệnh khác rm, nếu bạn có GNU find, sau đó -exec some_command {} +nhóm thành các lô như xargs, thay vào đó là \;hành vi chạy lệnh riêng cho từng lệnh.
Peter Cordes

@lsd findchạy lệnh trên mỗi tệp khi và chỉ khi nó sử dụng -exec command \;Cả hai xargs-exec command \+sẽ gọi lệnh với số lượng đối số tối đa được hệ thống cho phép. Nói cách khác, chúng tương đương nhau
Sergiy Kolodyazhnyy

6

xargs lấy đầu vào tiêu chuẩn của nó và biến nó thành dòng lệnh args.

find . -name '*.c' | xargs grep 'stdlib.h' rất giống với

grep 'stdlib.h' $(find . -name '*.c')  # UNSAFE, DON'T USE

Và sẽ cho kết quả tương tự miễn là danh sách tên tệp không quá dài cho một dòng lệnh. (Linux hỗ trợ megabyte văn bản trên một dòng lệnh, do đó, thông thường bạn không cần xargs.)


Nhưng cả hai đều hút, bởi vì chúng bị hỏng nếu tên tệp của bạn chứa khoảng trắng . Thay vào đó, find -print0 | xargs -0hoạt động, nhưng cũng vậy

find . -name '*.c' -exec grep 'stdlib.h' {} +

Điều đó không bao giờ đặt tên tập tin ở bất cứ đâu: findgộp chúng thành một dòng lệnh lớn và chạy greptrực tiếp.

\;thay vì +chạy grep riêng cho từng tệp, tốc độ chậm hơn nhiều. Đừng làm vậy. Nhưng +là một phần mở rộng GNU, vì vậy bạn cần xargsthực hiện việc này một cách hiệu quả nếu bạn không thể giả sử GNU tìm thấy.


Nếu bạn rời khỏi xargs, find | grepmẫu của nó có khớp với danh sách tên tệp được findin không.

Vì vậy, tại thời điểm đó, bạn cũng có thể làm find -name stdlib.h. Tất nhiên, với -name '*.c' -name stdlib.h, bạn sẽ không nhận được bất kỳ đầu ra nào vì cả hai mẫu đó không thể khớp và hành vi mặc định của nó là VÀ các quy tắc cùng nhau.

Thay thế lesstại bất kỳ điểm nào trong quy trình để xem sản lượng nào của bất kỳ phần nào của đường ống sản xuất.


Đọc thêm: http://mywiki.wooledge.org/BashFAQ có một số công cụ tuyệt vời.


1
GNU xargs cũng -dphải đặt dấu phân cách, do đó bạn có thể sử dụng -d'\n'để xử lý danh sách phân tách dòng mới, có thể hữu ích nếu bạn xử lý danh sách tên tệp trong tệp, v.v. (miễn là tên tệp không có dòng mới trong đó, đó là.)
ilkkachu

@ilkkachu: yeah, dòng mới trong tên tệp hiếm hơn rất nhiều so với khoảng trắng, vì chúng phá vỡ hầu hết các tập lệnh. myfunc(){ local IFS=$'\n'; fgrep stdlib.h` $ (find) ; }cũng hoạt động với hiệu ứng tương tự. Hoặc là một lớp lót, một (IFS=...; cmd...)lớp con cũng hoạt động để chứa thay đổi đối với IFS mà không phải lưu / khôi phục nó.
Peter Cordes

@PeterCordes Xin đừng làm command $( find )loại công cụ. Tên tập tin có vấn đề với không gian và các ký tự đặc biệt có thể phá vỡ loại điều này. Ít nhất là gấp đôi trích dẫn thay thế lệnh.
Sergiy Kolodyazhnyy

@SergiyKolodyazhnyy: Cảm ơn bạn đã chỉ ra rằng có vẻ như tôi thực sự khuyên bạn nên làm điều đó. Mọi người lướt qua có thể đã sao chép / dán thay vì đọc phần tiếp theo. Cập nhật để giải quyết điều đó.
Peter Cordes

@SergiyKolodyazhnyy: Hay bạn đang trả lời bình luận của tôi? Lưu ý rằng tôi đặt IFSđể nó tương đương với việc sử dụng xargs '-d\n'. Mở rộng Glob và xử lý metacharacter vỏ xảy ra trước các hiệu ứng thay thế lệnh, vì vậy tôi nghĩ rằng nó an toàn ngay cả với tên tệp có chứa $()hoặc >. Đồng ý rằng sử dụng chia tách từ thay thế lệnh không phải là cách thực hành tốt ngoại trừ sử dụng tương tác một lần trong đó bạn biết điều gì đó về tên tệp. Nhưng command "$(find)"chỉ hữu ích nếu bạn mong đợi nó tạo ra chính xác 1 tên tệp ...
Peter Cordes

5

Nói chung, xargsđược sử dụng cho các trường hợp bạn chuyển ống (có ký hiệu |) một cái gì đó từ lệnh này sang lệnh khác ( Command1 | Command2), nhưng đầu ra từ lệnh đầu tiên không được nhận chính xác làm đầu vào cho lệnh thứ hai.

Điều này thường xảy ra khi lệnh thứ hai không xử lý chính xác dữ liệu đầu vào thông qua Standard In (stdin) (ví dụ: Nhiều dòng làm đầu vào, cách các dòng được thiết lập, các ký tự được sử dụng làm đầu vào, nhiều tham số làm đầu vào, loại dữ liệu nhận được dưới dạng đầu vào, v.v.). Để cho bạn một ví dụ nhanh, hãy kiểm tra như sau:

Ví dụ 1:

ls | echo- Điều này sẽ không làm gì cả vì echokhông biết cách xử lý đầu vào mà anh ta đang nhận. Bây giờ trong trường hợp này nếu chúng ta sử dụng xargsnó sẽ xử lý đầu vào theo cách có thể được xử lý chính xác bằng cách echo(ví dụ: Như một dòng thông tin)

ls | xargs echo- Điều này sẽ xuất tất cả thông tin từ lstrong một dòng

Ví dụ 2:

Giả sử tôi có nhiều tệp goLang trong một thư mục có tên là go. Tôi sẽ tìm kiếm chúng với một cái gì đó như thế này:

find go -name *.go -type f | echo- Nhưng nếu biểu tượng đường ống ở đó và echoở cuối, nó sẽ không hoạt động.

find go -name *.go -type f | xargs echo- Ở đây nó sẽ hoạt động nhờ xargsnhưng nếu tôi muốn mỗi phản hồi từ findlệnh trong một dòng, tôi sẽ làm như sau:

find go -name *.go -type f | xargs -0 echo- Trong trường hợp này, cùng một đầu ra từ findsẽ được hiển thị bởi echo.

Các lệnh như cp, echo, rm, lessvà các lệnh khác cần một cách tốt hơn để xử lý đầu vào sẽ nhận được lợi ích khi sử dụng xargs.


4

xargs được sử dụng để tự động tạo đối số dòng lệnh dựa trên (thường) trên danh sách các tệp.

Vì vậy, xem xét một số lựa chọn thay thế để sử dụng xargslệnh tiếp theo:

find . -name '*.c' -print0 | xargs -0 grep 'stdlib.h'

Có một số lý do để sử dụng nó thay vì các tùy chọn khác không được đề cập ban đầu trong các câu trả lời khác:

  1. find . -name '*.c' -exec grep 'stdlib.h' {}\;sẽ tạo ra một grepquy trình cho mỗi tệp. Điều này thường được coi là thực hành xấu và có thể gây ra gánh nặng lớn cho hệ thống nếu có nhiều tệp được tìm thấy.
  2. Nếu có nhiều tệp, một grep 'stdlib.h' $(find . -name '*.c')lệnh có thể sẽ thất bại, vì đầu ra của $(...)thao tác sẽ vượt quá độ dài dòng lệnh tối đa của trình bao

Như đã đề cập trong các câu trả lời khác, lý do sử dụng -print0đối số findtrong kịch bản này và -0đối số cho xargs, để tên tệp có một số ký tự nhất định (ví dụ dấu ngoặc kép, dấu cách hoặc thậm chí dòng mới) vẫn được xử lý chính xác.

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.