Tạo xargs thực thi lệnh một lần cho mỗi dòng đầu vào


341

Làm cách nào để tạo xargs thực thi lệnh chính xác một lần cho mỗi dòng đầu vào đã cho? Hành vi mặc định của nó là chunk các dòng và thực thi lệnh một lần, chuyển nhiều dòng cho mỗi thể hiện.

Từ http://en.wikipedia.org/wiki/Xargs :

tìm / đường dẫn -type f -print0 | xargs -0 rm

Trong ví dụ này, tìm nguồn cấp dữ liệu đầu vào của xargs với một danh sách dài các tên tệp. xargs sau đó chia danh sách này thành danh sách phụ và gọi rm một lần cho mỗi danh sách phụ. Đây là hiệu quả hơn so với phiên bản chức năng tương đương này:

tìm / đường dẫn -type f -exec rm '{}' \;

Tôi biết rằng find có cờ "exec". Tôi chỉ trích dẫn một ví dụ minh họa từ một tài nguyên khác.


4
Trong ví dụ bạn cung cấp, find /path -type f -deletesẽ còn hiệu quả hơn nữa :)
tzot

cố gắng không sử dụng xargs ...
Naib

6
OP, tôi biết câu hỏi này rất cũ, nhưng nó vẫn xuất hiện trên Google và IMHO câu trả lời được chấp nhận là sai. Xem câu trả lời dài hơn của tôi dưới đây.
Tobia

Vui lòng xem xét chuyển đổi chấp nhận của bạn sang câu trả lời của @ Tobia, điều này tốt hơn nhiều. Câu trả lời được chấp nhận không xử lý khoảng trắng trong tên và không cho phép nhiều đối số cho lệnh xargs, một trong những tính năng chính của xargs.
Xám

Câu trả lời:


391

Những điều sau đây sẽ chỉ hoạt động nếu bạn không có khoảng trắng trong đầu vào của mình:

xargs -L 1
xargs --max-lines=1 # synonym for the -L option

từ trang người đàn ông:

-L max-lines
          Use at most max-lines nonblank input lines per command line.
          Trailing blanks cause an input line to be logically continued  on
          the next input line.  Implies -x.

13
Đối với tôi nó có thể giống xargs -n 1như cái bạn đã đưa ra "danh sách đối số quá dài".
Wernight

19
Nếu MAX-LINESđược bỏ qua, nó mặc định là 1, như vậy xargs -llà đủ. Xem info xargs.
Thor

3
@Wernight: "-n1" không cung cấp 1 lời gọi cho mỗi dòng đầu vào. có thể dòng đầu vào của bạn quá dài bản demo : echo "foo bar" | xargs -n1 echo. do đó, nếu bạn đặt những thứ như 'ls', nó sẽ không xử lý tốt các không gian.
gatoatigrado

8
Cái này sai. -L 1không trả lời câu hỏi ban đầu, và chỉ -n 1làm như vậy trong một trong những cách giải thích có thể. Xem câu trả lời dài của tôi dưới đây.
Tobia

2
@Tobia: Nó trả lời câu hỏi ban đầu, khá cụ thể về các dòng đầu vào. Đó chính xác là những gì -L 1. Đối với tôi, OP dường như rõ ràng đang cố gắng tránh hành vi chunk mặc định, và vì điều này được chấp nhận nên tôi cho rằng mình đã đúng. Câu trả lời của bạn giải quyết một trường hợp sử dụng hơi khác trong đó bạn cũng muốn hành vi chunking.
Draemon

206

Dường như với tôi tất cả các câu trả lời hiện có trên trang này đều sai, bao gồm cả câu trả lời là đúng. Điều đó xuất phát từ thực tế là câu hỏi được diễn đạt mơ hồ.

Tóm tắt:   Nếu bạn muốn thực thi lệnh "chính xác một lần cho mỗi dòng đầu vào đã cho", chuyển toàn bộ dòng (không có dòng mới) cho lệnh dưới dạng một đối số, thì đây là cách tương thích UNIX tốt nhất để thực hiện:

... | tr '\n' '\0' | xargs -0 -n1 ...

GNU xargscó thể có hoặc không có các phần mở rộng hữu ích cho phép bạn loại bỏ tr, nhưng chúng không có sẵn trên OS X và các hệ thống UNIX khác.

Bây giờ cho lời giải thích dài


Có hai vấn đề cần tính đến khi sử dụng xargs:

  1. làm thế nào để phân chia đầu vào thành "đối số"; và
  2. Có bao nhiêu đối số để truyền lệnh con tại một thời điểm.

Để kiểm tra hành vi của xargs, chúng tôi cần một tiện ích cho biết số lần thực thi của nó và với bao nhiêu đối số. Tôi không biết nếu có một tiện ích tiêu chuẩn để làm điều đó, nhưng chúng ta có thể mã hóa nó khá dễ dàng trong bash:

#!/bin/bash
echo -n "-> "; for a in "$@"; do echo -n "\"$a\" "; done; echo

Giả sử bạn lưu nó như showtrong thư mục hiện tại của bạn và làm cho nó có thể thực thi được, đây là cách nó hoạt động:

$ ./show one two 'three and four'
-> "one" "two" "three and four" 

Bây giờ, nếu câu hỏi ban đầu thực sự là về điểm 2. ở trên (như tôi nghĩ là vậy, sau khi đọc nó vài lần) và nó sẽ được đọc như thế này (thay đổi in đậm):

Làm cách nào để tạo xargs thực thi lệnh chính xác một lần cho mỗi đối số của đầu vào đã cho? Hành vi mặc định của nó là phân đoạn đầu vào thành các đối số và thực thi lệnh càng nhiều lần càng tốt , chuyển nhiều đối số cho mỗi trường hợp.

thì câu trả lời là -n 1.

Hãy so sánh hành vi mặc định của xargs, phân tách đầu vào xung quanh khoảng trắng và gọi lệnh càng nhiều lần càng tốt:

$ echo one two 'three and four' | xargs ./show 
-> "one" "two" "three" "and" "four" 

và hành vi của nó với -n 1:

$ echo one two 'three and four' | xargs -n 1 ./show 
-> "one" 
-> "two" 
-> "three" 
-> "and" 
-> "four" 

Mặt khác, nếu câu hỏi ban đầu là về điểm 1. phân tách đầu vào và nó được đọc như thế này (nhiều người đến đây dường như nghĩ rằng đó là trường hợp, hoặc gây nhầm lẫn cho hai vấn đề):

Làm cách nào để tạo xargs thực thi lệnh với chính xác một đối số cho mỗi dòng đầu vào đã cho? Hành vi mặc định của nó là chunk các dòng xung quanh khoảng trắng .

sau đó câu trả lời là tinh tế hơn.

Mọi người sẽ nghĩ rằng điều đó -L 1có thể giúp ích, nhưng hóa ra nó không thay đổi phân tích cú pháp đối số. Nó chỉ thực thi lệnh một lần cho mỗi dòng đầu vào, với càng nhiều đối số có trên dòng đầu vào đó:

$ echo $'one\ntwo\nthree and four' | xargs -L 1 ./show 
-> "one" 
-> "two" 
-> "three" "and" "four" 

Không chỉ vậy, nhưng nếu một dòng kết thúc bằng khoảng trắng, nó sẽ được thêm vào dòng tiếp theo:

$ echo $'one \ntwo\nthree and four' | xargs -L 1 ./show 
-> "one" "two" 
-> "three" "and" "four" 

Rõ ràng, -Lkhông phải là về việc thay đổi cách xargs chia đầu vào thành các đối số.

Đối số duy nhất làm như vậy theo kiểu đa nền tảng (không bao gồm các phần mở rộng GNU) là -0, phân tách đầu vào xung quanh các byte NUL.

Sau đó, đó chỉ là vấn đề dịch các dòng mới sang NUL với sự trợ giúp của tr:

$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 ./show 
-> "one " "two" "three and four" 

Bây giờ phân tích cú pháp đối số trông ổn, bao gồm cả khoảng trắng theo sau.

Cuối cùng, nếu bạn kết hợp kỹ thuật này với -n 1, bạn sẽ nhận được chính xác một lệnh thực thi cho mỗi dòng đầu vào, bất kể đầu vào nào bạn có, có thể là một cách khác để xem câu hỏi ban đầu (có thể là trực quan nhất, được đặt tiêu đề):

$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 -n1 ./show 
-> "one " 
-> "two" 
-> "three and four" 

Có vẻ như đây là câu trả lời tốt hơn. tuy nhiên, tôi vẫn không hiểu sự khác biệt giữa -L và -n ... bạn có thể giải thích thêm một chút không?
olala

5
@olala -Lthực thi lệnh một lần trên mỗi dòng đầu vào (nhưng một khoảng trắng ở cuối dòng nối nó với dòng tiếp theo và dòng vẫn được chia thành các đối số theo khoảng trắng); trong khi -nthực thi lệnh một lần cho mỗi đối số đầu vào. Nếu bạn đếm số lượng ->trong các ví dụ đầu ra, đó là số lần tập lệnh ./showđược thực thi.
Tobia

tôi hiểu rồi! không nhận ra một khoảng trống ở cuối dòng nối nó với dòng tiếp theo. cảm ơn!
olala

4
GNU xargscó thể có hoặc không có các tiện ích mở rộng hữu ích cho phép bạn loại bỏtr Nó có một tiện ích mở rộng rất hữu ích; từ xargs --help- -d, --d006iter = CHARACTER các mục trong luồng đầu vào được phân tách bằng CHARACTER, không phải bằng khoảng trắng; vô hiệu hóa trích dẫn và xử lý dấu gạch chéo ngược và xử lý EOF hợp lý
Piotr Dobrogost

Câu trả lời này có vẻ bối rối liên quan -L. -Lkhông nói có bao nhiêu lần để thực thi tập lệnh trên mỗi dòng, nó cho biết có bao nhiêu dòng dữ liệu đầu vào để tiêu thụ tại một thời điểm.
Moberg

22

Nếu bạn muốn chạy lệnh cho mọi dòng (tức là kết quả) đến từ đâu find, thì bạn cần cái xargsgì?

Thử:

find đường dẫn -type f -exec lệnh của bạn {} \;

trong đó chữ {}được thay thế bằng tên tệp và nghĩa đen \;là cần thiết findđể biết rằng lệnh tùy chỉnh kết thúc ở đó.

BIÊN TẬP:

(sau khi chỉnh sửa câu hỏi của bạn làm rõ rằng bạn biết về -exec)

Từ man xargs:

-L max-lines
Sử dụng tối đa các dòng đầu vào không trống tối đa trên mỗi dòng lệnh. Khoảng trống lưu trữ làm cho một dòng đầu vào được tiếp tục một cách hợp lý trên dòng đầu vào tiếp theo. Ngụ ý -x.

Lưu ý rằng tên tệp kết thúc bằng khoảng trống sẽ gây rắc rối cho bạn nếu bạn sử dụng xargs:

$ mkdir /tmp/bax; cd /tmp/bax
$ touch a\  b c\  c
$ find . -type f -print | xargs -L1 wc -l
0 ./c
0 ./c
0 total
0 ./b
wc: ./a: No such file or directory

Vì vậy, nếu bạn không quan tâm đến -exectùy chọn, bạn nên sử dụng -print0-0:

$ find . -type f -print0 | xargs -0L1 wc -l
0 ./c
0 ./c
0 ./b
0 ./a

17

Làm cách nào để tạo xargs thực thi lệnh chính xác một lần cho mỗi dòng đầu vào đã cho?

-L 1là giải pháp đơn giản nhưng nó không hoạt động nếu bất kỳ tệp nào chứa khoảng trắng trong đó. Đây là chức năng chính của -print0đối số find - để phân tách các đối số theo ký tự '\ 0' thay vì khoảng trắng. Đây là một ví dụ:

echo "file with space.txt" | xargs -L 1 ls
ls: file: No such file or directory
ls: with: No such file or directory
ls: space.txt: No such file or directory

Một giải pháp tốt hơn là sử dụng trđể chuyển đổi dòng mới thành \0ký tự null ( ) và sau đó sử dụng xargs -0đối số. Đây là một ví dụ:

echo "file with space.txt" | tr '\n' '\0' | xargs -0 ls
file with space.txt

Nếu sau đó bạn cần giới hạn số lượng cuộc gọi, bạn có thể sử dụng -n 1đối số để thực hiện một cuộc gọi đến chương trình cho mỗi đầu vào:

echo "file with space.txt" | tr '\n' '\0' | xargs -0 -n 1 ls

Điều này cũng cho phép bạn lọc đầu ra của find trước khi chuyển đổi các ngắt thành null.

find . -name \*.xml | grep -v /target/ | tr '\n' '\0' | xargs -0 tar -cf xml.tar

1
Có lỗi cú pháp trong khối mã thứ hai tr '\ n' '\ 0 \ => tr' \ n '' \ 0 ', tôi đã cố sửa lỗi này nhưng "Chỉnh sửa phải có ít nhất 6 ký tự" (có vẻ như là ngu ngốc như git từ chối cam kết vì sự thay đổi của tôi ít hơn 6 ký tự)
htaccess

1
Điều này có nghĩa là gì: "Một vấn đề khác khi sử dụng -Lcũng là nó không cho phép nhiều đối số cho mỗi xargslệnh gọi."?
Moberg

Tôi đã cải thiện câu trả lời của mình để xóa thông tin không liên quan đó @Moberg.
Xám

11

Một cách khác ...

find /path -type f | while read ln; do echo "processing $ln"; done

9

Hai cách này cũng hoạt động và sẽ hoạt động đối với các lệnh khác không sử dụng find!

xargs -I '{}' rm '{}'
xargs -i rm '{}'

trường hợp sử dụng ví dụ:

find . -name "*.pyc" | xargs -i rm '{}'

sẽ xóa tất cả các tệp pyc trong thư mục này ngay cả khi các tệp pyc chứa khoảng trắng.


Điều này phát sinh một cuộc gọi tiện ích cho mọi yếu tố không tối ưu.
Xám

7
find path -type f | xargs -L1 command 

là tất cả những gì bạn cần.


4

Lệnh sau sẽ tìm tất cả các tệp (-type f) trong /pathđó sao chép chúng bằng cpthư mục hiện tại. Lưu ý việc sử dụng if -I %để chỉ định một ký tự giữ chỗ trong cpdòng lệnh để có thể đặt các đối số sau tên tệp.

find /path -type f -print0 | xargs -0 -I % cp % .

Đã thử nghiệm với xargs (GNU findutils) 4.4.0


2

Bạn có thể giới hạn số lượng dòng hoặc đối số (nếu có khoảng trắng giữa mỗi đối số) bằng cách sử dụng các cờ --max-lines hoặc --max-args.

  -L max-lines
         Use at most max-lines nonblank input lines per command line.  Trailing blanks cause an input line to be logically continued on the next  input
         line.  Implies -x.

  --max-lines[=max-lines], -l[max-lines]
         Synonym  for  the -L option.  Unlike -L, the max-lines argument is optional.  If max-args is not specified, it defaults to one.  The -l option
         is deprecated since the POSIX standard specifies -L instead.

  --max-args=max-args, -n max-args
         Use at most max-args arguments per command line.  Fewer than max-args arguments will be used if the size (see  the  -s  option)  is  exceeded,
         unless the -x option is given, in which case xargs will exit.

0

Có vẻ như tôi không đủ danh tiếng để thêm nhận xét vào câu trả lời của Tobia ở trên , vì vậy tôi đang thêm "câu trả lời" này để giúp những người trong chúng ta muốn thử nghiệm xargscùng một cách trên nền tảng Windows.

Dưới đây là tệp bó của windows thực hiện tương tự như tập lệnh "hiển thị" được mã hóa nhanh của Tobia:

@echo off
REM
REM  cool trick of using "set" to echo without new line
REM  (from:  http://www.psteiner.com/2012/05/windows-batch-echo-without-new-line.html)
REM
if "%~1" == "" (
    exit /b
)

<nul set /p=Args:  "%~1"
shift

:start
if not "%~1" == "" (
    <nul set /p=, "%~1"
    shift
    goto start
)
echo.

0

Câu trả lời @Draemon dường như đúng với "-0" ngay cả với khoảng trống trong tệp.

Tôi đã thử lệnh xargs và tôi thấy rằng "-0" hoạt động hoàn hảo với "-L". thậm chí các không gian được xử lý (nếu đầu vào bị hủy kết thúc). Sau đây là một ví dụ :

#touch "file with space"
#touch "file1"
#touch "file2"

Sau đây sẽ phân chia null và thực thi lệnh trên mỗi đối số trong danh sách:

 #find . -name 'file*' -print0 | xargs -0 -L1
./file with space
./file1
./file2

vì vậy -L1sẽ thực thi đối số trên mỗi ký tự kết thúc null nếu được sử dụng với "-0". Để xem sự khác biệt hãy thử:

 #find . -name 'file*' -print0 | xargs -0 | xargs -L1
 ./file with space ./file1 ./file2

thậm chí điều này sẽ thực thi một lần:

 #find . -name 'file*' -print0  | xargs -0  | xargs -0 -L1
./file with space ./file1 ./file2

Lệnh sẽ thực thi một lần vì "-L" bây giờ không phân tách trên byte null. bạn cần cung cấp cả "-0" và "-L" để hoạt động.


-3

Trong ví dụ của bạn, điểm của đường ống đầu ra của find to xargs là hành vi tiêu chuẩn của tùy chọn -exec của find là thực thi lệnh một lần cho mỗi tệp tìm thấy. Nếu bạn đang sử dụng find và bạn muốn hành vi tiêu chuẩn của nó, thì câu trả lời rất đơn giản - đừng sử dụng xargs để bắt đầu.


Trên thực tế, điều tôi có thể ám chỉ từ các chỉnh sửa của OP là dữ liệu đầu vào không liên quan gì findvà đó là lý do tại sao họ không thích -exectùy chọn này.
tzot

-3

thực thi tác vụ ant-clean-all trên mọi build.xml trên thư mục hiện tại hoặc thư mục con.

find . -name 'build.xml' -exec ant -f {} clean-all \;

Không phải ai cũng đã antcài đặt.
Xám
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.