Truyền đầu ra của lệnh trước sang tiếp theo làm đối số


119

Tôi có một lệnh xuất dữ liệu ra thiết bị xuất chuẩn ( command1 -p=aaa -v=bbb -i=4). Dòng đầu ra có thể có giá trị sau:

rate (10%) - name: value - 10Kbps

Tôi muốn grep đầu ra đó để lưu trữ 'tỷ lệ' đó (tôi đoán đường ống sẽ hữu ích ở đây). Và cuối cùng, tôi muốn tỷ lệ đó là giá trị của một tham số trên lệnh thứ hai (giả sử command2 -t=${rate})

Có vẻ là khó khăn về phía tôi; Tôi muốn biết rõ hơn làm thế nào để sử dụng ống, grep, sed và như vậy.

Tôi đã thử rất nhiều kết hợp như thế nhưng tôi cảm thấy bối rối về những điều này:

$ command1 -p=aaa -v=bbb -i=4 | grep "rate" 2>&1 command2 -t="rate was "${rate}

Câu trả lời:


144

Bạn đang nhầm lẫn hai loại đầu vào rất khác nhau.

  1. Đầu vào tiêu chuẩn ( stdin)
  2. Đối số dòng lệnh

Đây là khác nhau, và hữu ích cho các mục đích khác nhau. Một số lệnh có thể nhận đầu vào theo cả hai cách, nhưng chúng thường sử dụng chúng khác nhau. Lấy ví dụ wclệnh:

  1. Vượt qua đầu vào bằng stdin:

    ls | wc -l
    

    Điều này sẽ đếm các dòng trong đầu ra của ls

  2. Truyền đầu vào bằng đối số dòng lệnh:

    wc -l $(ls)
    

    Điều này sẽ đếm các dòng trong danh sách các tệp được in bởils

Những điều hoàn toàn khác nhau.

Để trả lời câu hỏi của bạn, có vẻ như bạn muốn nắm bắt tốc độ từ đầu ra của lệnh đầu tiên, sau đó sử dụng tốc độ làm đối số dòng lệnh cho lệnh thứ hai. Đây là một cách để làm điều đó:

rate=$(command1 | sed -ne 's/^rate..\([0-9]*\)%.*/\1/p')
command2 -t "rate was $rate"

Giải thích về sed:

  • Các s/pattern/replacement/lệnh là để thay thế một số mô hình
  • Mẫu có nghĩa là: dòng phải bắt đầu bằng "Rate" ( ^rate) theo sau bởi bất kỳ hai ký tự ( ..), theo sau là 0 hoặc nhiều chữ số, theo sau là a %, theo sau là phần còn lại của văn bản ( .*)
  • \1trong thay thế có nghĩa là nội dung của biểu thức đầu tiên được ghi lại bên trong \(...\), vì vậy trong trường hợp này, các chữ số trước %dấu hiệu
  • Các -nlá cờ của sedlệnh có nghĩa là không in dòng theo mặc định. Ở pcuối s///lệnh có nghĩa là in dòng nếu có sự thay thế. Nói tóm lại, lệnh sẽ chỉ in một cái gì đó nếu có khớp.

13

Tôi có xu hướng sử dụng điều này:

command1 | xargs -I{} command2 {}

Truyền đầu ra của command1xargs bằng cách sử dụng thay thế (dấu ngoặc) cho command2. Nếu lệnh1 findchắc chắn sử dụng -print0và thêm -0vào xargscho các chuỗi kết thúc null và xargssẽ gọi command2cho từng thứ được tìm thấy.

Trong trường hợp của bạn (và lấy dòng sed từ @Janos):

command1 -p=aaa -v=bbb -i=4 | sed -ne 's/^rate..\([0-9]*\)%.*/\1/p' | xargs -I{} command2 -t="rate was {}"

Điều này giữ cho mọi thứ tốt đẹp và ống, đó là lý do tại sao tôi thích điều này.
Mateen Ulhaq

4

Để mô phỏng đầu ra của command1tôi đang sử dụng câu lệnh echo này:

$ echo -e "Foo\nrate (10%) - name: value - 10Kbps\nBar"
$ alias command1='echo -e "Blah\nrate (10%) - name: value - 10Kbps\nBlag"'

Một bài kiểm tra nhanh:

$ command1
Blah
rate (10%) - name: value - 10Kbps
Blag

Đó là tất cả tốt, vì vậy hãy phân tích nó ra:

$ command1 | grep 'rate'
rate (10%) - name: value - 10Kbps

Vì vậy, chúng tôi nhận được dòng chúng tôi muốn command1, hãy chuyển nó vào command2:

$ alias command2='echo'
$ command2 -t="rate was "$(command1 | grep 'rate')
-t=rate was rate (10%) - name: value - 10Kbps

Tôi đang mong đợi được "rate was "$(command1 | grep 'rate')ghép nối tự động. Nếu điều đó không hoạt động vì khoảng trắng, thay vào đó, bạn có thể chuyển đầu vào như vậy:

$ alias command2='echo'
$ command2 -t=$(echo '"rate was ' $(command1 | grep 'rate') '"')
-t="rate was rate (10%) - name: value - 10Kbps "


2

Nhiệm vụ đầu tiên là trích xuất tỷ lệ từ dòng đó. Với GNU grep (Linux không nhúng hoặc Cygwin), bạn có thể sử dụng -otùy chọn. Phần bạn muốn là phần chỉ chứa các chữ số và theo sau là một %dấu hiệu. Nếu bạn không muốn giải nén %chính nó, bạn cần một mẹo bổ sung: một xác nhận nhìn rộng bằng 0 , không khớp với gì ngoài chỉ khi không có gì được theo sau %.

command1 -p=aaa -v=bbb -i=4 | grep -o -P '[0-9]+(?=%)'

Một khả năng khác là sử dụng sed. Để trích xuất một phần của dòng trong sed, sử dụng slệnh, với biểu thức chính phù hợp với toàn bộ dòng (bắt đầu bằng ^và kết thúc bằng $), với phần được giữ lại trong một nhóm ( \(…\)). Thay thế toàn bộ dòng bằng nội dung của nhóm để giữ. Nói chung, chuyển -ntùy chọn tắt in mặc định và đặt công cụ psửa đổi để in các dòng có nội dung cần trích xuất (ở đây có một dòng duy nhất để nó không thành vấn đề). Xem Return chỉ một phần của một dòng sau khi một mô hình phù hợpExtracting một regex phù hợp với 'sed' mà không cần in các nhân vật xung quanh để biết thêm thủ thuật sed.

command1 -p=aaa -v=bbb -i=4 | sed 's/^.*rate(\([0-9]*\)%).*$/\1/'

Linh hoạt hơn so với sed, là awk. Awk thực hiện các hướng dẫn cho mỗi dòng trong một ngôn ngữ mệnh lệnh nhỏ. Có nhiều cách để trích xuất tỷ lệ ở đây; Tôi chọn các trường thứ hai (các trường được phân cách bằng khoảng trắng theo mặc định) và xóa tất cả các ký tự trong đó không phải là một chữ số.

command1 -p=aaa -v=bbb -i=4 | awk '{gsub(/[^0-9]+/, "", $2); print $2}'

Bước tiếp theo, bây giờ bạn đã trích xuất tỷ lệ, là chuyển nó làm đối số command2. Các công cụ cho đó là một susbtlation lệnh . Nếu bạn đặt một lệnh bên trong $(…)(dấu ngoặc đơn), đầu ra của nó được thay thế vào dòng lệnh. Đầu ra của lệnh được chia thành các từ riêng biệt ở mỗi khối khoảng trắng và mỗi từ được coi là một mẫu ký tự đại diện; trừ khi bạn muốn điều này xảy ra, hãy đặt dấu ngoặc kép xung quanh lệnh thay thế : "$(…)". Với dấu ngoặc kép, đầu ra của lệnh được sử dụng trực tiếp dưới dạng một tham số duy nhất (biến đổi duy nhất là các dòng mới ở cuối đầu ra được loại bỏ).

command2 -t "$(command1 -p=aaa -v=bbb -i=4 |
               sed 's/^.*rate(\([0-9]*\)%).*$/\1/')"

0

Bạn có thể sử dụng grepvà đó là PCRE - Biểu thức chính quy tương thích Perl. Điều này cho phép bạn sử dụng giao diện để khớp với giá trị của tỷ lệ, mà không bao gồm chuỗi "tỷ lệ" trong kết quả của bạn khi grepping cho nó.

Thí dụ

$ echo "rate (10%) - name: value - 10Kbps" | grep -oP '(?<=^rate \()\d+'
10

Chi tiết

Các grepcông việc trên như sau:

  • -osẽ chỉ trả lại những gì chúng tôi đang tìm kiếm \d+, đó là các chữ số trong các ô.
  • -P cho phép tính năng PCRE của grep
  • (?<=^rate \() sẽ chỉ trả về các chuỗi bắt đầu bằng "tỷ lệ ("

lệnh2

Để bắt được giá trị của "tỷ lệ", bạn có thể chạy command1như vậy:

$ rate=$(command 1 | grep -oP '(?<=^rate \()\d+'

Sau đó, cho lệnh thứ 2 của bạn, bạn chỉ cần sử dụng biến đó.

$ command2 -t=${rate}

Bạn có thể nhận được ưa thích và làm cho tất cả một dòng:

$ command2 -t=$(command1 | grep -oP '(?<=^rate \()\d+')

Điều này sẽ thực thi lệnh1 bên trong $(..)khối thực thi, lấy kết quả của nó và đưa chúng vào -t=..chuyển đổi của lệnh2 .


0

Tôi thường sử dụng `lệnh` để đặt đầu ra của nó làm đối số cho lệnh khác. Ví dụ: để tìm tài nguyên được tiêu thụ bởi quá trình foo trên freebsd sẽ là:

procstat -r `pgrep -x foo`

Ở đây, pgrep được sử dụng để trích xuất PID của quy trình foo được truyền cho lệnh Procstat, dự kiến ​​PID của quy trình là đối số.

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.