ส่งผ่านเอาต์พุตของคำสั่งก่อนหน้าไปยังอาร์กิวเมนต์


118

ฉันคำสั่งที่ส่งออกข้อมูลไปยัง stdout ( command1 -p=aaa -v=bbb -i=4) บรรทัดเอาต์พุตสามารถมีค่าต่อไปนี้:

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

ฉันต้องการ grep ผลลัพธ์นั้นเพื่อเก็บ 'rate' (ฉันเดาว่า pipe จะมีประโยชน์ที่นี่) และสุดท้ายฉันต้องการให้อัตรานั้นเป็นค่าของพารามิเตอร์ในคำสั่งที่สอง (สมมุติว่าcommand2 -t=${rate})

ดูเหมือนว่าจะเป็นเรื่องยุ่งยากในด้านของฉัน ฉันต้องการทราบวิธีการใช้ท่อ, grep, sed และอื่น ๆ ที่ดีกว่า

ฉันได้ลองชุดค่าผสมจำนวนมากเช่นนั้นมาแล้ว แต่ฉันรู้สึกสับสนกับสิ่งเหล่านี้:

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

คำตอบ:


143

คุณกำลังสับสนอินพุตที่แตกต่างกันสองประเภท

  1. อินพุตมาตรฐาน ( stdin)
  2. อาร์กิวเมนต์บรรทัดคำสั่ง

สิ่งเหล่านี้แตกต่างกันและมีประโยชน์สำหรับวัตถุประสงค์ที่ต่างกัน คำสั่งบางคำสั่งสามารถรับอินพุตได้ทั้งสองวิธี แต่โดยทั่วไปจะใช้คำสั่งต่างกัน ยกตัวอย่างwcคำสั่ง:

  1. ผ่านการป้อนข้อมูลโดยstdin:

    ls | wc -l
    

    นี่จะนับบรรทัดในเอาต์พุตของ ls

  2. ผ่านอินพุตโดยอาร์กิวเมนต์บรรทัดคำสั่ง:

    wc -l $(ls)
    

    นี่จะนับบรรทัดในรายการไฟล์ที่พิมพ์โดยls

สิ่งที่แตกต่างอย่างสิ้นเชิง

เพื่อตอบคำถามของคุณดูเหมือนว่าคุณต้องการจับภาพอัตราจากผลลัพธ์ของคำสั่งแรกแล้วใช้อัตรานั้นเป็นอาร์กิวเมนต์บรรทัดคำสั่งสำหรับคำสั่งที่สอง นี่เป็นวิธีหนึ่งในการทำเช่นนั้น:

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

คำอธิบายของsed:

  • s/pattern/replacement/คำสั่งคือการเปลี่ยนรูปแบบบาง
  • รูปแบบหมายถึง: บรรทัดต้องเริ่มต้นด้วย "rate" ( ^rate) ตามด้วยอักขระสองตัวใด ๆ ( ..) ตามด้วยตัวเลข 0 หรือมากกว่าตามด้วย a %ตามด้วยข้อความที่เหลือ ( .*)
  • \1ในการแทนที่หมายถึงเนื้อหาของการแสดงออกครั้งแรกที่จับภายใน\(...\)ดังนั้นในกรณีนี้ตัวเลขก่อนที่%เครื่องหมาย
  • -nธงของsedคำสั่งหมายถึงการไม่พิมพ์บรรทัดโดยค่าเริ่มต้น pในตอนท้ายของs///หมายถึงคำสั่งที่จะพิมพ์บรรทัดถ้ามีการเปลี่ยน กล่าวโดยย่อคำสั่งจะพิมพ์บางอย่างเฉพาะเมื่อมีการแข่งขัน

13

ฉันมักจะใช้สิ่งนี้:

command1 | xargs -I{} command2 {}

ผ่านการส่งออกของcommand1ผ่าน xargs ใช้ทดแทน (วงเล็บ) command2เพื่อ หาก Command1 คือfindตรวจสอบการใช้-print0และเพิ่ม-0การxargsfor null สิ้นสุดสตริงและxargsจะเรียกcommand2สำหรับแต่ละสิ่งที่พบ

ในกรณีของคุณ (และรับสาย sed จาก @Janos):

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

สิ่งนี้ทำให้สิ่งที่ดีและไปป์ไลน์ซึ่งเป็นสาเหตุที่ฉันชอบสิ่งนี้
Mateen Ulhaq

4

เพื่อจำลองผลลัพธ์ของcommand1ฉันกำลังใช้คำสั่ง echo นี้:

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

การทดสอบอย่างรวดเร็ว:

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

นั่นคือทั้งหมดที่ดีดังนั้นให้แยกมันออกมา:

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

ดังนั้นเราจึงได้เส้นที่เราต้องการจากcommand1นั้นให้ผ่านเข้าไปในcommand2:

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

ฉันคาดว่า"rate was "$(command1 | grep 'rate')จะต่อกันโดยอัตโนมัติ หากวิธีนี้ใช้ไม่ได้เนื่องจากช่องว่างคุณควรส่งผ่านอินพุตแทนดังนี้:

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


2

ภารกิจแรกคือการดึงอัตราจากบรรทัดนั้น ด้วย GNU grep (ไม่ฝัง Linux หรือ Cygwin) คุณสามารถใช้-oตัวเลือก ส่วนที่คุณต้องการคือชิ้นส่วนที่มีตัวเลขเท่านั้นและตามด้วย%เครื่องหมาย หากคุณไม่ต้องการที่จะดึง%ตัวเองคุณจำเป็นต้องมีเคล็ดลับเพิ่มเติมที่: ยืนยัน lookahead ศูนย์ความกว้างซึ่งตรงกับอะไร %แต่ถ้าอะไรที่จะตามมาด้วย

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

ความเป็นไปได้อีกอย่างคือการใช้ sed เพื่อแยกส่วนของบรรทัดใน sed ใช้sคำสั่งด้วย regex ที่ตรงกับทั้งบรรทัด (เริ่มต้นด้วย^และลงท้ายด้วย$) กับส่วนที่จะเก็บไว้ในกลุ่ม ( \(…\)) แทนที่ทั้งบรรทัดด้วยเนื้อหาของกลุ่มเพื่อเก็บ โดยทั่วไปให้ส่ง-nตัวเลือกเพื่อปิดการพิมพ์เริ่มต้นและวางโมดิpฟายเออร์เพื่อพิมพ์บรรทัดที่มีบางสิ่งที่ต้องการแยก (ที่นี่มีบรรทัดเดียวดังนั้นจึงไม่สำคัญ) ดูที่คืนค่าเฉพาะบางส่วนของบรรทัดหลังจากรูปแบบการจับคู่และแยก regex ที่จับคู่กับ 'sed' โดยไม่ต้องพิมพ์อักขระโดยรอบเพื่อเพิ่มเทคนิคพิเศษ

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

มีความยืดหยุ่นมากขึ้นอีกครั้งกว่า sed, awk Awk ดำเนินการคำแนะนำสำหรับแต่ละบรรทัดด้วยภาษาที่จำเป็นเล็กน้อย มีหลายวิธีในการแยกอัตราที่นี่ ฉันเลือกเขตข้อมูลที่สอง (เขตข้อมูลถูกคั่นด้วยช่องว่างตามค่าเริ่มต้น) และลบอักขระทั้งหมดในนั้นที่ไม่ใช่ตัวเลข

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

command2ขั้นตอนต่อไปตอนนี้ที่คุณได้สกัดอัตราที่เป็นที่จะผ่านมันเป็นอาร์กิวเมนต์ไปยัง เครื่องมือสำหรับการว่าเป็นsusbtitution คำสั่ง หากคุณวางคำสั่งไว้ภายใน$(…)(ดอลล่าร์ - วงเล็บ) เอาต์พุตจะถูกแทนที่ลงในบรรทัดคำสั่ง เอาต์พุตของคำสั่งแบ่งออกเป็นคำที่แยกกันในแต่ละบล็อกช่องว่างและแต่ละคำจะถือว่าเป็นรูปแบบสัญลักษณ์แทน "$(…)"ถ้าคุณต้องการนี้จะเกิดขึ้นใส่คำพูดสองรอบทดแทนคำสั่ง: ด้วยเครื่องหมายคำพูดคู่เอาต์พุตของคำสั่งจะถูกใช้โดยตรงเป็นพารามิเตอร์เดียว (การแปลงเพียงอย่างเดียวคือการขึ้นบรรทัดใหม่ที่ตอนท้ายของเอาต์พุตจะถูกลบออก)

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

0

คุณสามารถใช้grepและเป็น PCRE - นิพจน์ปกติที่เข้ากันได้ของ Perl สิ่งนี้ช่วยให้คุณสามารถใช้ lookbehind เพื่อให้ตรงกับค่าของอัตราโดยไม่รวมสตริง "rate" ไว้ในผลลัพธ์ของคุณเมื่อทำการ grepping

ตัวอย่าง

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

รายละเอียด

grepงานดังกล่าวข้างต้น:

  • -oจะกลับเฉพาะสิ่งที่เรากำลังค้นหา\d+ซึ่งเป็นตัวเลขภายใน parens
  • -P เปิดใช้งานคุณสมบัติ PCRE ของ grep
  • (?<=^rate \() จะส่งคืนสตริงที่ขึ้นต้นด้วย "rate ("

Command2

ในการรับค่า "rate" คุณสามารถเรียกใช้command1ดังนี้:

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

จากนั้นสำหรับคำสั่งที่ 2 ของคุณคุณเพียงแค่ใช้ตัวแปรนั้น

$ command2 -t=${rate}

คุณสามารถจินตนาการและสร้างมันขึ้นมาหนึ่งบรรทัด:

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

สิ่งนี้จะดำเนินการ command1 ภายใน$(..)บล็อกการดำเนินการใช้ผลลัพธ์และรวมไว้ใน-t=..สวิตช์ของ command2


0

ฉันมักจะใช้คำสั่ง `` เพื่อวางมันเอาท์พุทเป็นข้อโต้แย้งกับคำสั่งอื่น ตัวอย่างเช่นหากต้องการค้นหาทรัพยากรที่ใช้โดยกระบวนการ foo บน freebsd จะเป็น:

procstat -r `pgrep -x foo`

ที่นี่ pgrep ใช้เพื่อแยก PID ของกระบวนการ foo ซึ่งส่งผ่านไปยังคำสั่ง procstat ซึ่งคาดว่า PID ของกระบวนการเป็นอาร์กิวเมนต์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.