Làm thế nào để áp dụng lệnh shell cho mỗi dòng của một đầu ra lệnh?


192

Giả sử tôi có một số đầu ra từ một lệnh (chẳng hạn như ls -1):

a
b
c
d
e
...

Tôi muốn áp dụng một lệnh (nói echo) cho từng người, lần lượt. Ví dụ

echo a
echo b
echo c
echo d
echo e
...

Cách dễ nhất để làm điều đó trong bash là gì?


3
ls -1có thể là một ví dụ ở đây nhưng điều quan trọng cần nhớ là không tốt khi phân tích đầu ra của ls. Xem: mywiki.wooledge.org/ParsingLs
codeforester

Câu trả lời:


217

Nó có lẽ dễ sử dụng nhất xargs. Trong trường hợp của bạn:

ls -1 | xargs -L1 echo

Các -Lcờ đảm bảo đầu vào được đọc đúng cách. Từ trang người đàn ông của xargs:

-L number
    Call utility for every number non-empty lines read. 
    A line ending with a space continues to the next non-empty line. [...]

24
lstự động làm -1trong một đường ống.
Tạm dừng cho đến khi có thông báo mới.

5
@Dennis, không giống như vậy: ls | xargs -L2 echols -1 | xargs -L2 echođưa ra hai kết quả đầu ra khác nhau. Các cựu là tất cả trên một dòng.
Alex Budovski

2
@Alex: Tôi nhận được cùng một đầu ra.
Tạm dừng cho đến khi có thông báo mới.

6
xargschỉ có thể chạy các tệp thực thi chứ không phải các hàm shell hoặc các lệnh dựng sẵn. Đối với trước đây, giải pháp tốt nhất có lẽ là một readtrong một vòng lặp.
pabouk

12
Tôi muốn câu trả lời này bao gồm một lời giải thích về những gì -L1là cho.
Wyck

161

Bạn có thể sử dụng thao tác trả trước cơ bản trên mỗi dòng:

ls -1 | while read line ; do echo $line ; done

Hoặc bạn có thể chuyển đầu ra sang sed cho các hoạt động phức tạp hơn:

ls -1 | sed 's/^\(.*\)$/echo \1/'

1
Lệnh sed dường như không hoạt động: sh: cho: not found a sh: cho: not foundCó vẻ như nó lấy etiếng vang là lệnh sed hay gì đó.
Alex Budovski

+1 cho whilevòng lặp. cmd1 | while read line; do cmd2 $line; done. Hoặc while read line; do cmd2 $line; done < <(cmd1)không tạo ra một subshell. Đây là phiên bản đơn giản hóa của sedlệnh của bạn :sed 's/.*/echo &/'
Tạm dừng cho đến khi có thông báo mới.

1
@Alex: thay đổi dấu ngoặc kép thành dấu ngoặc đơn.
Tạm dừng cho đến khi có thông báo mới.

5
Trích dẫn "$line"trong vòng lặp while, để tránh chia tách từ.
ignis

3
Hãy thử sử dụng read -r lineđể tránh readgây rối với các ký tự thoát. Ví dụ echo '"a \"nested\" quote"' | while read line; do echo "$line"; donecho "a "nested" quote", mà đã mất thoát. Nếu chúng ta làm, echo '"a \"nested\" quote"' | while read -r line; do echo "$line"; donechúng ta nhận được "a \"nested\" quote"như mong đợi. Xem wiki.bash-hackers.org/commands/builtin/read
Warbo 7/07/2015

9

Bạn có thể sử dụng vòng lặp for :

cho tệp trong *; làm
   tiếng vang "tập tin $"
làm xong

Lưu ý rằng nếu lệnh trong câu hỏi chấp nhận nhiều đối số, thì việc sử dụng xargs hầu như luôn hiệu quả hơn vì nó chỉ phải sinh ra tiện ích trong câu hỏi một lần thay vì nhiều lần.


1
Thật đáng để mô tả việc sử dụng xargs đúng cách / an toàn, tức là. printf '%s\0' * | xargs -0 ...- nếu không, nó khá không an toàn với tên tệp có khoảng trắng, dấu ngoặc kép, v.v.
Charles Duffy

8

Bạn thực sự có thể sử dụng sed để làm điều đó, với điều kiện đó là GNU sed.

... | sed 's/match/command \0/e'

Làm thế nào nó hoạt động:

  1. Trận đấu thay thế với trận đấu lệnh
  2. Trên lệnh thực thi thay thế
  3. Thay thế dòng thay thế bằng đầu ra lệnh.

Tuyệt diệu. Tôi đã quên đặt lệnh e ở cuối, nhưng đã làm như vậy sau khi thấy câu trả lời của bạn và nó đã hoạt động. Tôi đã cố gắng thêm một ID ngẫu nhiên trong khoảng từ 1000 đến 15000, khi SED khớp với một dòng. cat /logs/lfa/Modified.trace.log.20150904.pw | sed -r 's/^(.*)(\|006\|00032\|)(.*)$/echo "\1\2\3 - ID `shuf -i 999-14999 -n 1`"/e'
sgsi


2

xargs thất bại với dấu gạch chéo ngược, dấu ngoặc kép. Nó cần phải là một cái gì đó như

ls -1 |tr \\n \\0 |xargs -0 -iTHIS echo "THIS is a file."

tùy chọn xargs -0:

-0, --null
          Input  items are terminated by a null character instead of by whitespace, and the quotes and backslash are
          not special (every character is taken literally).  Disables the end of file string, which is treated  like
          any  other argument.  Useful when input items might contain white space, quote marks, or backslashes.  The
          GNU find -print0 option produces input suitable for this mode.

ls -1chấm dứt các mục với các ký tự dòng mới, do đó trchuyển chúng thành các ký tự null.

Cách tiếp cận này chậm hơn khoảng 50 lần so với lặp lại thủ công với for ...(xem câu trả lời của Michael Aaron Safyan ) (3,55 giây so với 0,066 giây). Nhưng đối với các lệnh đầu vào khác như định vị, tìm, đọc từ tệp ( tr \\n \\0 <file) hoặc tương tự, bạn phải làm việc với xargsnhư thế này.


1

Kết quả tốt hơn cho tôi:

ls -1 | xargs -L1 -d "\n" CMD

Tốt hơn, nhưng không hoàn hảo. find . -mindepth 1 -maxdepth 1 -print0 | xargs -0 commandsẽ xử lý các trường hợp đầu ra của ls -1mơ hồ; sử dụng -printf '%P\0'thay vì -print0nếu bạn không muốn dẫn đầu ./về mỗi.
Charles Duffy

1

tôi thích sử dụng gawk để chạy nhiều lệnh trong danh sách, ví dụ

ls -l | gawk '{system("/path/to/cmd.sh "$1)}'

tuy nhiên việc thoát khỏi các nhân vật có thể thoát được có thể có một ít lô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.