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 xargs
có 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:
- làm thế nào để phân chia đầu vào thành "đối số"; và
- 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ư show
trong 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 1
có 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, -L
khô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"
find /path -type f -delete
sẽ còn hiệu quả hơn nữa :)