Lewati output dari perintah sebelumnya ke berikutnya sebagai argumen


119

Saya punya perintah yang menampilkan data ke stdout ( command1 -p=aaa -v=bbb -i=4). Baris output dapat memiliki nilai berikut:

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

Saya ingin menangkap output itu untuk menyimpan 'rate' itu (saya kira pipa akan berguna di sini). Dan akhirnya, saya ingin menilai itu menjadi nilai parameter pada perintah kedua (katakanlah command2 -t=${rate})

Tampaknya rumit di pihak saya; Saya ingin tahu lebih baik bagaimana menggunakan pipa, grep, sed dan sebagainya.

Saya sudah mencoba banyak kombinasi seperti itu tetapi saya bingung akan hal ini:

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

Jawaban:


144

Anda membingungkan dua jenis input yang sangat berbeda.

  1. Input standar ( stdin)
  2. Argumen baris perintah

Ini berbeda, dan berguna untuk tujuan yang berbeda. Beberapa perintah dapat mengambil input dalam dua cara, tetapi mereka biasanya menggunakannya secara berbeda. Ambil contoh wcperintahnya:

  1. Lulus masukan dengan stdin:

    ls | wc -l
    

    Ini akan menghitung garis dalam output dari ls

  2. Melewati input dengan argumen baris perintah:

    wc -l $(ls)
    

    Ini akan menghitung baris dalam daftar file yang dicetak olehls

Hal yang sangat berbeda.

Untuk menjawab pertanyaan Anda, sepertinya Anda ingin menangkap laju dari output perintah pertama, dan kemudian menggunakan kursor sebagai argumen baris perintah untuk perintah kedua. Inilah satu cara untuk melakukan itu:

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

Penjelasan tentang sed:

  • The s/pattern/replacement/perintah untuk mengganti beberapa pola
  • Polanya berarti: baris harus dimulai dengan "rate" ( ^rate) diikuti oleh dua karakter ( ..), diikuti oleh 0 atau lebih digit, diikuti oleh %, diikuti oleh sisa teks ( .*)
  • \1dalam penggantian berarti isi dari ekspresi pertama yang ditangkap di dalam \(...\), jadi dalam hal ini digit sebelum %tanda
  • The -nbendera sedperintah berarti untuk tidak mencetak baris secara default. The pdi akhir s///perintah berarti untuk mencetak garis jika ada penggantinya. Singkatnya, perintah akan mencetak sesuatu hanya jika ada kecocokan.

13

Saya cenderung menggunakan ini:

command1 | xargs -I{} command2 {}

Pass output dari command1xargs menggunakan substitusi (kawat gigi) ke command2. Jika command1 adalah findpastikan untuk menggunakan -print0dan menambahkan -0ke xargsnull dihentikan string dan xargsakan memanggil command2untuk setiap hal yang ditemukan.

Dalam kasus Anda (dan mengambil garis sed dari @Janos):

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

Ini menjaga hal-hal bagus dan pipa, itulah sebabnya saya suka ini.
Mateen Ulhaq

4

Untuk mensimulasikan output command1saya menggunakan pernyataan gema ini:

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

Tes cepat:

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

Itu semua bagus, jadi mari kita uraikan:

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

Jadi kita mendapatkan garis yang kita inginkan command1, mari kita sampaikan ke command2:

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

Saya berharap "rate was "$(command1 | grep 'rate')untuk menyatukan secara otomatis. Jika itu tidak berhasil karena spasi, Anda seharusnya dapat meneruskan input seperti itu sebagai gantinya:

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


2

Tugas pertama adalah mengekstraksi kurs dari baris itu. Dengan GNU grep (Linux yang tidak tertanam atau Cygwin), Anda dapat menggunakan -oopsi ini. Bagian yang Anda inginkan adalah bagian yang hanya mengandung digit, dan diikuti oleh %tanda. Jika Anda tidak ingin mengekstraksi %sendiri, Anda perlu trik tambahan: pernyataan lookahead lebar nol , yang tidak cocok dengan apa pun kecuali hanya jika tidak ada yang diikuti %.

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

Kemungkinan lain adalah menggunakan sed. Untuk mengekstrak bagian dari baris dalam sed, gunakan sperintah, dengan regex yang cocok dengan seluruh baris (dimulai dengan ^dan diakhiri dengan $), dengan bagian untuk mempertahankan dalam grup ( \(…\)). Ganti seluruh baris dengan konten grup yang ingin dipertahankan. Secara umum, berikan -nopsi untuk mematikan pencetakan default dan letakkan ppengubah untuk mencetak garis di mana ada sesuatu untuk diekstraksi (di sini ada satu baris sehingga tidak masalah). Lihat Mengembalikan hanya sebagian dari garis setelah pola yang cocok dan Mengekstrak regex yang cocok dengan 'sed' tanpa mencetak karakter di sekitarnya untuk trik sed yang lebih banyak.

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

Lebih fleksibel lagi daripada sed, awk. Awk menjalankan instruksi untuk setiap baris dalam bahasa imperatif kecil. Ada banyak cara untuk mengekstraksi kurs di sini; Saya memilih bidang kedua (bidang dibatasi oleh spasi putih secara default), dan menghapus semua karakter di dalamnya yang bukan angka.

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

Langkah selanjutnya, sekarang setelah Anda mengekstraksi kurs, adalah meneruskannya sebagai argumen command2. Alat untuk itu adalah susbtitusi perintah . Jika Anda memasukkan perintah ke dalam $(…)(dolar-kurung), hasilnya diganti ke baris perintah. Output dari perintah dibagi menjadi kata-kata yang terpisah di setiap blok spasi, dan setiap kata diperlakukan sebagai pola wildcard; kecuali jika Anda ingin ini terjadi, menempatkan tanda kutip ganda sekitar substitusi perintah: "$(…)". Dengan tanda kutip ganda, output dari perintah digunakan secara langsung sebagai parameter tunggal (satu-satunya transformasi adalah bahwa baris baru di akhir output dihapus).

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

0

Anda dapat menggunakan grepdan itu PCRE - Ekspresi Reguler Kompatibel Kompatibel. Ini memungkinkan Anda untuk menggunakan tampilan di belakang untuk mencocokkan nilai laju, tanpa menyertakan "nilai" string dalam hasil Anda saat mengambilnya.

Contoh

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

Detail

Di atas grepberfungsi sebagai berikut:

  • -ohanya akan mengembalikan apa yang kita cari \d+, yang merupakan digit di dalam parens.
  • -P mengaktifkan fitur PCRE grep
  • (?<=^rate \() hanya akan mengembalikan string yang dimulai dengan "rate ("

perintah2

Untuk menangkap nilai "rate" Anda dapat menjalankan command1seperti:

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

Kemudian untuk perintah 2 Anda, Anda cukup menggunakan variabel itu.

$ command2 -t=${rate}

Anda bisa menjadi mewah dan membuat itu semua menjadi satu baris:

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

Ini akan mengeksekusi command1 di dalam $(..)blok eksekusi, mengambil hasilnya dan memasukkannya ke dalam -t=..saklar command2 .


0

Saya biasanya menggunakan `command` untuk menempatkan output sebagai argumen ke perintah lain. Misalnya, untuk menemukan sumber daya yang dikonsumsi oleh proses foo di freebsd adalah:

procstat -r `pgrep -x foo`

Di sini, pgrep digunakan untuk mengekstrak PID dari proses foo yang dilewatkan ke perintah procstat yang mengharapkan PID proses sebagai argumen.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.