it-swarm-vi.com

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

Tôi muốn chạy lệnh Java một lần cho mỗi trận đấu của 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.)

176
Xodarap

Đó là những gì xargs làm.

... | xargs command
98
Keith

Câu trả lời được chấp nhận có ý tưởng đúng, nhưng chìa khóa là vượt qua xargs the -n1 switch, có nghĩa là "Thực thi 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 đường ống từ cat hoàn toàn và chỉ cần đi với:

<file xargs -n1 command
193
Michael Goldshteyn

Trong Bash hoặc bất kỳ Shell 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 (read không có -r giải thích dấ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
117
Steven D

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 Nice 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 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.

22
Johan

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

15
Ole Tange

Cũng thế, while read loop trong fish Shell (Tôi giả sử bạn muốn Shell Shell, xem xét bạn đã sử dụng fish tag).

command | while read line
    command $line
end

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

  • read không mất -r đối số 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, không giống như bash, cá không tách các biến theo không gian.
  • command tự nó là một lỗi cú pháp (để bắt sử dụng các đối số giữ chỗ như vậy). Thay thế nó bằng lệnh thực sự.
10
Konrad Borowski

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 trong vim, và xâu chuỗi nó bằng tìm kiếm và thay thế các biểu thức 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 (nghĩa là /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 Edge như Van Gogh, O'Connors, St. Clair, Smith-Wesson.

Đang có set hlsearch rất hữu ích để thực hiện việc này trong vim, vì 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, thì 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.

1
Marcin

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.

1
DustWolf

Điều này có thể với xargs, như các câu trả lời khác được chỉ ra. Chúng ta cần phân biệt hai chi tiết cụ thể trong phần "một lần trên mỗi dòng" của câu hỏi:

  1. Một lần: Sử dụng -n 1, điều này đảm bảo rằng lệnh được gọi chính xác một lần cho mỗi đối số. Tuy nhiên, theo mặc định, xargs giả sử rằng các đối số được phân tách bằng dấu cách - điều này sẽ bị phá vỡ khi các tệp chứa khoảng trắng.
  2. Mỗi dòng: Sử dụng -d '\n' hoặc tiền xử lý đầu vào với tr '\n' '\0' Và sử dụng -0. Điều này làm cho lệnh mạnh mẽ chống lại không gian trong đầu vào.

Dòng lệnh cuối cùng sau đó trở thành:

.... | xargs -n 1 -d '\n' <command>

hoặc (với tr)

.... | tr '\n' '\0' | xargs -n 1 -0 <command>

Nếu lệnh của bạn có thể xử lý nhiều đối số cùng một lúc (như grep hoặc sed), bạn có thể bỏ qua -n 1 để tăng tốc độ hoạt động trong nhiều trường hợp.

0
krlmlr

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
/

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

0
Rolf

Tôi thích điều này - cho phép các lệnh nhiều dòng và mã rõ ràng

find -type f -name filenam-pattern* | while read -r F
do
  echo $F
  cat $F | grep 'some text'
done

tham chiếu https://stackoverflow.com/a/3891678/248616

0
Nam G VU