Thực hiện một lệnh một lần trên mỗi dòng đầu vào đường ống?


162

Tôi muốn chạy một lệnh java một lần cho mỗi trận đấu ls | grep pattern -. Trong trường hợp này, tôi nghĩ rằng tôi có thể làm find pattern -exec java MyProg '{}' \;nhưng tôi tò mò về trường hợp chung - có cách nào dễ dàng để nói "chạy lệnh một lần cho mỗi dòng đầu vào tiêu chuẩn" không? (Trong cá hoặc bash.)

Câu trả lời:


92

Đó là những gì xargs.

... | xargs command

25
Không hẳn. printf "foo bar\nbaz bat" | xargs echo wheesẽ mang lại whee foo bar baz bat. Có thể thêm các tùy chọn -Lhoặc -n?
Jander

3
@Jander Câu hỏi khá chung chung, vì vậy tôi đã đưa ra công cụ chung. Đúng, bạn sẽ phải điều chỉnh hành vi của nó với các tùy chọn tùy thuộc vào hoàn cảnh cụ thể.
Keith

4
... | tr '\ n' '\ 0' | xargs -0
vrdhn

7
như, "hoàn cảnh cụ thể đưa ra câu trả lời đúng cho câu hỏi". :)
mattdm

7
Nếu bạn muốn xem cách thích hợp để làm điều này với xargs, hãy xem câu trả lời của tôi dưới đây.
Michael Goldshteyn

167

Câu trả lời được chấp nhận có ý tưởng đúng, nhưng điều quan trọng là vượt qua xargscông -n1tắc, có nghĩa là "Thực hiện lệnh một lần trên mỗi dòng đầu ra:"

cat file... | xargs -n1 command

Hoặc, đối với một tệp đầu vào, bạn có thể tránh cathoàn toàn đường ống và chỉ cần đi với:

<file xargs -n1 command

1
Cũng được quan tâm là khả năng của xargsđể không chạy nếu stdintrống: --no-run-if-empty -r: Nếu đầu vào tiêu chuẩn không chứa bất kỳ nonblanks, đừng chạy lệnh. Thông thường, lệnh được chạy một lần ngay cả khi không có đầu vào. Tùy chọn này là một phần mở rộng GNU.
Ronan Jouchet

4
Làm thế nào dyou truy cập vào dòng bên trong command?
BT

Đây là cách sử dụng chính xác của xargs. Không có -n1, nó chỉ hoạt động trên các lệnh xử lý danh sách các tham số dưới dạng nhiều lệnh mà không phải tất cả đều thực hiện.
masterxilo

3
printf "thanh foo \ nbaz bat" | xargs -n1 echo whee chia tách bằng lời nói chứ không phải theo dòng
Gismo Ranas

112

Trong Bash hoặc bất kỳ vỏ kiểu Bourne nào khác (tro, ksh, zsh, Nhận):

while read -r line; do command "$line"; done

read -rđọc một dòng từ đầu vào tiêu chuẩn ( readkhông có -rdấu gạch chéo ngược, bạn không muốn điều đó). Vì vậy, bạn có thể làm một trong những điều sau đây:

$ command | while read -r line; do command "$line"; done  

$ while read -r line; do command "$line"; done <file

6
Khi tôi thử tail -f syslog | grep -e something -e somethingelse| while read line; do echo $line; donenó không hoạt động. Nó làm việc với một tập tin được đưa vào whilevòng lặp, làm việc với chỉ tail -f, làm việc với chỉ grep, nhưng không phải với cả hai ống. Đưa ra grepcác --line-bufferedtùy chọn làm cho nó làm việc

Điều này cũng hoạt động khi mỗi dòng cần được gửi đến stdin:command | while read -r line; do echo "$line" | command ; done
Den

21

Tôi đồng ý với Keith, xargs là công cụ chung nhất cho công việc.

Tôi thường sử dụng cách tiếp cận 3 bước.

  • làm những thứ cơ bản cho đến khi bạn có thứ gì đó bạn muốn làm việc cùng
  • chuẩn bị dòng với awk để nó có được cú pháp đúng
  • sau đó để xargs thực thi nó, có thể với sự trợ giúp của bash.

Có những cách nhỏ hơn và nhanh hơn, nhưng cách này hầu như luôn hoạt động.

Một ví dụ đơn giản:

ls | 
grep xls | 
awk '{print "MyJavaProg --arg1 42 --arg2 "$1"\0"}' | 
xargs -0 bash -c

2 dòng đầu tiên chọn một số tệp để làm việc, sau đó awk chuẩn bị một chuỗi đẹp với lệnh thực thi và một số đối số và $ 1 là đầu vào cột đầu tiên từ đường ống. Và cuối cùng tôi chắc chắn rằng xargs gửi chuỗi này đến bash mà chỉ cần thực thi nó.

Đó là một chút quá mức cần thiết, nhưng công thức này đã giúp tôi ở rất nhiều nơi vì nó rất linh hoạt.


6
Lưu ý, xargs -0sử dụng byte null làm dấu tách bản ghi, vì vậy câu lệnh in awk của bạn phải làprintf("MyJavaProg --args \"%s\"\0",$1)
glenn jackman

@glenn: Bỏ lỡ null char, sẽ cập nhật câu trả lời
Johan

@Johan không phải là vấn đề lớn, nhưng nếu bạn đang sử dụng, awkbạn có thể thực hiện mô hình khớp và bỏ qua grep ví dụ:ls | awk '/xls/ {print...
Eric Renouf

15

GNU Parallel được tạo cho loại nhiệm vụ đó. Cách sử dụng đơn giản nhất là:

cat stuff | grep pattern | parallel java MyProg

Xem video giới thiệu để tìm hiểu thêm: http://www.youtube.com/watch?v=OpaiGYxkSuQ


1
Không có nhu cầu thực sự catở đây vì grepcó thể trực tiếp đọc tệp
Eric Renouf


1
Cảm ơn về liên kết, tôi không nhất thiết phải đồng ý rằng nó dễ đọc hơn, nhưng thật tuyệt khi biết nó được xem xét bất kể. Bây giờ tôi chỉ hơi ngụy biện rằng liên kết không thực sự được áp dụng ở đây vì sự thay thế không thực sự < stuff grep patternnhưng hoàn toàn grep pattern stuffkhông có chuyển hướng hoặc yêu cầu mèo. Tuy nhiên, điều đó không thay đổi đáng kể đối số của bạn và nếu bạn nghĩ rõ ràng hơn là luôn luôn sử dụng mọi thứ trong một đường ống bắt đầu cat, thì hãy tiếp sức cho bạn
Eric Renouf

8

Ngoài ra, while readlặp trong vỏ cá (tôi giả sử bạn muốn có vỏ cá, xem xét bạn đã sử dụng thẻ ).

command | while read line
    command $line
end

Vài điểm cần lưu ý.

  • readkhông -rtranh luận và nó không diễn giải dấu gạch chéo ngược của bạn, để làm cho trường hợp sử dụng phổ biến nhất trở nên dễ dàng.
  • Bạn không cần trích dẫn $line, vì không giống như bash, cá không tách các biến theo không gian.
  • commandbởi chính nó là một lỗi cú pháp (để bắt như vậy sử dụng các đối số giữ chỗ). Thay thế nó bằng lệnh thực sự.

Không whilecần phải ghép nối với do& donethay vì end?
aff

@aff Đây là đặc biệt về vỏ cá, có cú pháp khác nhau.
Konrad Borowski

Ah, vậy đó là ý nghĩa của con cá.
aff

6

Nếu bạn cần kiểm soát chính xác nơi đối số đầu vào được chèn vào dòng lệnh của bạn hoặc nếu bạn cần lặp lại nó nhiều lần thì bạn cần sử dụng xargs -I{}.

VÍ DỤ 1

Tạo cấu trúc thư mục trống trong another_folderđó phản chiếu các thư mục con trong thư mục hiện tại:

    ls -1d ./*/ | xargs -I{} mkdir another_folder/{}
VÍ DỤ # 2

Áp dụng một thao tác trên danh sách tệp đến từ stdin, trong trường hợp này tạo một bản sao của mỗi .htmltệp bằng cách nối thêm .bakphần mở rộng:

    find . -iname "*.html" | xargs -I{} cp {} {}.bak

Từ xargstrang man cho MacOS / BSD :

 -I replstr
         Execute utility for each input line, replacing one or more occurrences of
         replstr in up to replacements (or 5 if no -R flag is specified) arguments
         to utility with the entire line of input.  The resulting arguments, after
         replacement is done, will not be allowed to grow beyond 255 bytes; this is
         implemented by concatenating as much of the argument containing replstr as
         possible, to the constructed arguments to utility, up to 255 bytes.  The
         255 byte limit does not apply to arguments to utility which do not contain
         replstr, and furthermore, no replacement will be done on utility itself.
         Implies -x.

xargsTrang người đàn ông Linux :

   -I replace-str
          Replace  occurrences of replace-str in the initial-
          arguments with names read from standard input.  Al
          so,  unquoted  blanks do not terminate input items;
          instead the separator  is  the  newline  character.
          Implies -x and -L 1.

1

Khi xử lý các đầu vào có khả năng không được xác nhận, tôi muốn thấy toàn bộ công việc 'đánh vần' từng dòng để kiểm tra trực quan trước khi tôi chạy nó (đặc biệt là khi đó là thứ gì đó phá hoại như làm sạch hộp thư của mọi người).

Vì vậy, những gì tôi làm là tạo một danh sách các tham số (ví dụ: tên người dùng), đưa nó vào một tệp theo kiểu một bản ghi trên mỗi dòng, như sau:

johndoe  
jamessmith  
janebrown  

Sau đó, tôi mở danh sách vimvà đọc nó với các biểu thức tìm kiếm và thay thế cho đến khi tôi nhận được một danh sách các lệnh đầy đủ cần được thực thi, như thế này:

/bin/rm -fr /home/johndoe  
/bin/rm -fr /home/jamessmith 

Bằng cách này nếu regex của bạn không đầy đủ, bạn sẽ thấy trong lệnh nào sẽ có vấn đề tiềm ẩn (ví dụ. /bin/rm -fr johnnyo connor). Bằng cách này, bạn có thể hoàn tác regex của mình và thử lại với phiên bản đáng tin cậy hơn. Tên mangling nổi tiếng về điều này, bởi vì thật khó để chăm sóc tất cả các trường hợp cạnh như Van Gogh, O'Connors, St. Clair, Smith-Wesson.

set hlsearchích khi thực hiện việc này vimvì nó sẽ làm nổi bật tất cả các trận đấu, vì vậy bạn có thể dễ dàng phát hiện ra nếu nó không khớp hoặc khớp theo cách không mong muốn.

Khi regex của bạn hoàn hảo và nó nắm bắt được tất cả các trường hợp bạn có thể kiểm tra / nghĩ ra, sau đó tôi thường chuyển đổi nó thành biểu thức sed để nó có thể hoàn toàn tự động cho một lần chạy khác.

Đối với trường hợp số lượng dòng đầu vào ngăn bạn thực hiện kiểm tra trực quan, tôi khuyên bạn nên lặp lại lệnh cho màn hình (hoặc tốt hơn là nhật ký) trước khi thực thi, vì vậy nếu nó bị lỗi, bạn sẽ biết chính xác lệnh nào gây ra nó thất bại Sau đó, bạn có thể quay lại regex ban đầu của mình và điều chỉnh một lần nữa.


0

Nếu một chương trình bỏ qua đường ống nhưng chấp nhận các tệp làm đối số, thì bạn chỉ có thể trỏ nó vào tệp đặc biệt /dev/stdin.

Tôi không quen thuộc với java, nhưng đây là một ví dụ về cách bạn sẽ làm điều đó cho bash:

$ echo $'pwd \n cd / \n pwd' |bash /dev/stdin
/home/rolf
/

$ Là cần thiết cho bash để dịch \nsang dòng mới. Tôi cung không chăc tại sao.



0

Tại đây, một bản sao bạn có thể sử dụng ngay:

cat list.txt | xargs -I{} command parameter {} parameter

Mục từ danh sách sẽ được đặt trong đó {} và phần còn lại của lệnh và tham số sẽ được sử dụng nguyên trạng.

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.