Khi nào cần xargs?


134

Các xargslệnh luôn bối rối cho tôi. Có một quy tắc chung cho nó?

Hãy xem xét hai ví dụ dưới đây:

$ \ls | grep Cases | less

in các tệp khớp với 'Trường hợp', nhưng thay đổi lệnh touchsẽ yêu cầu xargs:

$ \ls | grep Cases | touch
touch: missing file operand
Try `touch --help' for more information.

$ \ls | grep Cases | xargs touch

Câu trả lời:


143

Sự khác biệt là ở những dữ liệu mà chương trình mục tiêu đang chấp nhận.

Nếu bạn chỉ sử dụng một đường ống, nó sẽ nhận dữ liệu trên STDIN (luồng đầu vào tiêu chuẩn) dưới dạng một đống dữ liệu thô mà nó có thể sắp xếp qua một dòng tại một thời điểm. Tuy nhiên, một số chương trình không chấp nhận các lệnh của chúng theo tiêu chuẩn, chúng hy vọng nó sẽ được đánh vần trong các đối số của lệnh. Ví dụ: touchlấy tên tệp làm tham số trên dòng lệnh như vậy : touch file1.txt.

Nếu bạn có một chương trình mà kết quả đầu ra tên tập tin vào tiêu chuẩn ra và muốn sử dụng chúng như các đối số để touch, bạn phải sử dụng xargsmà đọc dữ liệu dòng STDIN và chuyển đổi mỗi dòng vào không gian lý lẽ tách ra lệnh.

Hai điều này là tương đương:

# touch file1.txt
# echo file1.txt | xargs touch

Đừng sử dụng xargstrừ khi bạn biết chính xác những gì nó đang làm và tại sao nó cần thiết. Thông thường, có một cách tốt hơn để thực hiện công việc hơn là sử dụng xargsđể buộc chuyển đổi. Quá trình chuyển đổi cũng đầy rẫy những cạm bẫy tiềm ẩn như thoát ra và mở rộng từ, v.v.


2
Cảnh báo cảm thấy một chuỗi nhỏ với tôi. Trong hai tùy chọn phổ biến để truyền luồng lên dòng lệnh ( xargs$(...)), xargs an toàn hơn nhiều so với thay thế lệnh. Và tôi không thể nhớ lại đã từng bắt gặp một tên tệp hợp pháp với một dòng mới trong đó. Không phải là các vấn đề cạm bẫy mở rộng và thoát từ với sự thay thế lệnh, không phải là xargs?
camh

6
@camh: Họ là những cạm bẫy tiềm năng với cả hai. Trong trình bao, bạn phải lo lắng về tên tệp bị phân chia trên khoảng trắng, tab và dòng mới. Trong xargs, bạn chỉ phải lo lắng về dòng mới. Trong xargs, nếu đầu ra của bạn được định dạng chính xác, bạn có thể chia các từ / tên tệp trên ký tự NUL thay thế ( xargs -0), rất hữu ích khi kết hợp với find -print0.
Ken Bloom

xargsgọi chương trình thông qua shell với các đối số được phân tách bằng dấu cách hoặc thực sự xây dựng danh sách đối số bên trong (ví dụ: để sử dụng với execv/ execp)?
gièm pha

1
Nó xây dựng nó bên trong và sử dụng execvp, vì vậy nó an toàn. Ngoài ra, GNU xargs (như được sử dụng trên Linux và một vài thứ khác) cho phép bạn chỉ định dòng mới là dấu phân cách của bạn -d \n, mặc dù Barg xargs (OSX et al) không xuất hiện để hỗ trợ tùy chọn này.
fluffy

72

Để mở rộng các câu trả lời đã được cung cấp, xargscó thể thực hiện một điều thú vị đang ngày càng trở nên quan trọng trong bối cảnh điện toán đa lõi và phân tán ngày nay: nó có thể xử lý các công việc song song.

Ví dụ:

$ find . -type f -name '*.wav' -print0 |xargs -0 -P 3 -n 1 flac -V8

sẽ mã hóa * .wav => * .flac, sử dụng ba quy trình cùng một lúc ( -P 3).


Ồ Tôi nên biết điều này một tuần trước khi tôi đang làm chính xác điều tương tự (ngoại trừ sử dụng OGG) với 50GiB WAV. :)
Alois Mahdal

Tại sao không sử dụng tham số -exec mà find có?
Evgeny

3
@Evgeny -execTham số sẽ không xử lý các công việc song song.
amphetamachine

Tốt để lưu ý rằng -0đối số đểxargs làm cho nó coi NULLký tự là dấu phân cách mục đầu vào. find -print0các mục được phân định bằng NULL. Đây là cách thực hành tuyệt vời cho tên tệp có thể chứa dấu cách, dấu ngoặc kép hoặc các ký tự đặc biệt khác.
Dan Dascalescu

24

xargs đặc biệt hữu ích khi bạn có một danh sách các filepath trên stdin và muốn làm gì đó với chúng. Ví dụ:

$ git ls-files "*.tex" | xargs -n 1 sed -i "s/color/colour/g"

Hãy xem xét từng bước này:

$ git ls-files "*.tex"
tex/ch1/intro.tex
tex/ch1/motivation.tex
....

Nói cách khác, đầu vào của chúng tôi là một danh sách các đường dẫn mà chúng tôi muốn làm gì đó.

Để tìm hiểu những gì xargs làm với các đường dẫn này, một mẹo hay là thêm vào echotrước lệnh của bạn, như vậy:

$ git ls-files "*.tex" | xargs -n 1 echo sed -i "s/color/colour/g"
sed -i "s/color/colour/g" tex/ch1/intro.tex
sed -i "s/color/colour/g" tex/ch1/motivation.tex
....

Đối -n 1số sẽ làm cho xargs biến mỗi dòng thành một lệnh của riêng nó. Các sed -i "s/color/colour/g"lệnh sẽ thay thế tất cả các lần xuất hiện của colorvới colourcho một file nào đó.

Lưu ý rằng điều này chỉ hoạt động nếu bạn không có bất kỳ khoảng trống nào trên đường dẫn của mình. Nếu bạn làm như vậy, bạn nên sử dụng các đường dẫn kết thúc null làm đầu vào cho xargs bằng cách chuyển -0cờ. Một ví dụ sử dụng sẽ là:

$ git ls-files -z "*.tex" | xargs -0 -n 1 sed -i "s/color/colour/g"

Điều này không giống như những gì chúng tôi đã mô tả ở trên, nhưng cũng hoạt động nếu một trong các đường dẫn có một khoảng trống trong đó.

Điều này hoạt động với bất kỳ lệnh nào tạo ra tên tệp như đầu ra như findhoặc locate. Nếu bạn tình cờ sử dụng nó trong kho git với rất nhiều tệp, thì có thể hiệu quả hơn khi sử dụng nó git grep -lthay vì git ls-files, như vậy:

$ git grep -l "color" "*.tex" | xargs -n 1 sed -i "s/color/colour/g"

Các git grep -l "color" "*.tex"lệnh sẽ đưa ra một danh sách các file "* .tex" chứa cụm từ "màu".


1
Đúng, nhưng nếu bạn đã học được điều này, bạn cũng nên tìm hiểu Tại sao việc lặp đi lặp lại tìm ra thực tiễn tồi?
tự đại diện

6

Đối số đầu tiên của bạn minh họa sự khác biệt khá tốt.

\ls | grep Cases | lesscho phép bạn duyệt danh sách các tên tệp được tạo bởi lsgrep. Không có vấn đề gì khi chúng là tên tệp, chúng chỉ là một số văn bản.

\ls | grep Cases | xargs lesscho phép bạn duyệt các tệp có tên được tạo bởi phần đầu tiên của lệnh. xargslấy một danh sách các tên tệp làm đầu vào và một lệnh trên dòng lệnh của nó và chạy lệnh với các tên tệp trên dòng lệnh của nó .

Khi xem xét sử dụng xargs, hãy ghi nhớ rằng họ hy vọng đầu vào được định dạng theo một cách kỳ lạ: khoảng trắng được phân định, với \, '"được sử dụng để trích dẫn (một cách không bình thường, bởi vì \không phải là đặc biệt dấu ngoặc kép bên trong). Chỉ sử dụng xargsnếu tên tệp của bạn không chứa khoảng trắng hoặc \'".


@Gilles: xargs-0, --nulltùy chọn giải quyết vấn đề về không gian (rất có thể tôi đã học được điều đó từ bạn :), vì vậy tôi cho rằng bạn đang đề cập đến một xargcuộc gọi không có tùy chọn , nhưng tôi cảm thấy khó hiểu khi bạn tham khảo các trích dẫn. Bạn có một liên kết hoặc một ví dụ liên quan đến điều đó? .. (ps. | xargs lesslà một "mẹo" tiện dụng +1 .. cảm ơn ..
Peter.O

4

Trong ví dụ của bạn, bạn hoàn toàn không cần sử dụng xargsfindsẽ thực hiện chính xác và an toàn những gì bạn muốn làm.

Chính xác những gì bạn muốn sử dụng findlà:

find -maxdepth 1 -name '*Cases*' -exec touch {} +

Trong ví dụ này -maxdepth 1có nghĩa là chỉ tìm kiếm trong thư mục hiện tại, không đi xuống bất kỳ thư mục con nào; theo mặc định find sẽ tìm trong tất cả các thư mục con (thường là những gì bạn muốn) trừ khi bạn ràng buộc nó với maxdepth. Các {}là tên của tập tin đó sẽ được thay thế vào chỗ của nó và +là một trong hai dấu hiệu end-of-lệnh, các con khác ;. Sự khác biệt giữa chúng là điều đó ;có nghĩa là thực thi lệnh trên mỗi tệp một lần, trong khi đó +có nghĩa là thực thi lệnh trên tất cả các tệp cùng một lúc. Tuy nhiên, lưu ý rằng trình bao của bạn có thể sẽ cố gắng diễn giải ;chính nó, vì vậy bạn sẽ cần phải thoát nó bằng một trong hai \;hoặc ';'. Vâng, findcó một số ít phiền toái như thế này, nhưng sức mạnh của nó nhiều hơn là bù đắp cho nó.

Cả hai findxargskhó khăn để học lúc đầu. Để giúp bạn tìm hiểu, xargshãy thử sử dụng -phoặc --interactivetùy chọn sẽ hiển thị cho bạn lệnh sắp thực hiện và nhắc bạn có muốn chạy hay không.

Tương tự như vậy với findbạn có thể sử dụng -okthay -execcho bạn để nhắc bạn có muốn chạy lệnh hay không.

Tuy nhiên, đôi khi findsẽ không thể làm mọi thứ bạn muốn và đó là nơi xargsxuất hiện. -execLệnh sẽ chỉ chấp nhận một trường hợp {}xuất hiện, vì vậy nếu bạn gặp lỗi, find -type f -exec cp {} {}.bak \;thay vào đó bạn có thể làm như vậy :find -type f -print0 | xargs -0 -l1 -IX cp X X.bak

Bạn có thể tìm hiểu thêm về Lệnh chạy trong hướng dẫn GNU Findutils .

Ngoài ra, tôi đã đề cập rằng findan toàn thực hiện những gì bạn muốn bởi vì khi bạn xử lý tệp bạn sẽ gặp phải khoảng trắng và các ký tự khác sẽ gây ra sự cố xargstrừ khi bạn sử dụng tùy chọn -0hoặc --nullcùng với thứ gì đó tạo ra các mục đầu vào bị chấm dứt bởi ký tự null của khoảng trắng.



Tên tệp @Wildcard có khoảng trắng hoặc ký tự như 'hoặc "có thể có vấn đề, trong khi đó findsẽ xử lý các trường hợp đó mà không gặp sự cố.
aculich

Vâng tôi biết. Xem câu trả lời của tôi cho câu hỏi liên kết . Tôi có lẽ nên đọc lại câu hỏi đó cho một câu trong phần bình luận ở trên, hoặc thêm cụm từ "Xem câu hỏi ..." trước nó. : D
Wildcard

1

xargs(cùng với find, sort, du, uniq, perlvà một vài người khác) chấp nhận một chuyển đổi dòng lệnh để nói "STDIN có một danh sách các tập tin, cách nhau bằng một NUL (0x00) byte". Điều này giúp bạn dễ dàng xử lý tên tệp có khoảng trắng và các ký tự vui nhộn khác trong đó. Tên tệp không chứa NUL.


2
Tôi nghĩ bạn có nghĩa là "tên tệp không thể chứa null."
amphetamachine
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.