bash รองรับการอ้างอิงกลับในการขยายพารามิเตอร์หรือไม่


15

ฉันมีชื่อตัวแปรdescrซึ่งสามารถมีสตริงBlah: -> r1-ae0-2 / [123], -> s7-Gi0-0-1:1-US / Fooฯลฯ ฉันต้องการที่จะได้รับ-> r1-ae0-2, -> s7-Gi0-0-1:1-USส่วนหนึ่งมาจากสตริง ในขณะนี้ฉันใช้descr=$(grep -oP '\->\s*\S+' <<< "$descr"สำหรับสิ่งนี้ มีวิธีที่ดีกว่าในการทำเช่นนี้? เป็นไปได้ไหมที่จะทำเช่นนี้กับการขยายพารามิเตอร์

คำตอบ:


20

ksh93และzshมีการกลับอ้างอิง (หรือมากกว่าถูกต้อง1การอ้างอิงไปยังกลุ่มการจับภาพในการทดแทน) การสนับสนุนภายในไม่${var/pattern/replacement}bash

ksh93:

$ var='Blah: -> r1-ae0-2 / [123]'
$ printf '%s\n' "${var/*@(->*([[:space:]])+([^[:space:]]))*/\1}"
-> r1-ae0-2

zsh:

$ var='Blah: -> r1-ae0-2 / [123]'
$ set -o extendedglob
$ printf '%s\n' "${var/(#b)*(->[[:space:]]#[^[:space:]]##)*/$match[1]}"
-> r1-ae0-2

( mkshman page ระบุด้วยว่าเวอร์ชันในอนาคตจะรองรับกับ${KSH_MATCH[1]}กลุ่มการจับภาพครั้งแรกยังไม่พร้อมให้บริการตั้งแต่วันที่ 2017-04-25)

อย่างไรก็ตามด้วยbashคุณสามารถทำสิ่งต่อไปนี้

$ [[ $var =~ -\>[[:space:]]*[^[:space:]]+ ]] &&
  printf '%s\n' "${BASH_REMATCH[0]}"
-> r1-ae0-2

ซึ่งจะดีกว่าเพราะจะตรวจสอบว่าพบลวดลายก่อนหรือไม่

หากระบบของคุณรองรับ\s/ \Sคุณสามารถทำสิ่งต่อไปนี้

re='->\s*\S+'
[[ $var =~ $re ]]

ด้วยzsh, คุณสามารถใช้ PCREs ได้อย่างเต็มประสิทธิภาพด้วย:

$ set -o rematchpcre
$ [[ $var =~ '->\s*\S+' ]] && printf '%s\n' $MATCH
-> r1-ae0-2

ด้วยzsh -o extendedglobดู:

$ printf '%s\n' ${(SM)var##-\>[[:space:]]#[^[:space:]]##}
-> r1-ae0-2

portably:

$ expr " $var" : '.*\(->[[:space:]]*[^[:space:]]\{1,\}\)'
-> r1-ae0-2

หากมีหลายรูปแบบในสตริงพฤติกรรมจะแตกต่างกันไปตามวิธีแก้ปัญหาทั้งหมด อย่างไรก็ตามจะไม่มีรายการใดรายการหนึ่งให้คุณคั่นรายการขึ้นบรรทัดใหม่ของรายการที่ตรงกันทั้งหมดใน GNU ของคุณgrepแก้ปัญหาชั่น

ในการทำเช่นนั้นคุณจะต้องวนซ้ำด้วยมือ ตัวอย่างเช่นด้วยbash:

re='(->\s*\S+)(.*)'
while [[ $var =~ $re ]]; do
  printf '%s\n' "${BASH_REMATCH[1]}"
  var=${BASH_REMATCH[2]}
done

ด้วยzsh, คุณสามารถใช้กลอุบายประเภทนี้เพื่อเก็บการแข่งขันทั้งหมดในอาเรย์:

set -o extendedglob
matches=() n=0
: ${var//(#m)->[[:space:]]#[^[:space:]]##/${matches[++n]::=$MATCH}}
printf '%s\n' $matches

1การอ้างอิงย้อนกลับมักจะกำหนดรูปแบบที่อ้างอิงสิ่งที่ถูกจับคู่โดยกลุ่มก่อนหน้า ตัวอย่างเช่น\(.\)\1นิพจน์ทั่วไปพื้นฐานจับคู่อักขระเดียวตามด้วยอักขระเดียวกันนั้น (ตรงกับaaไม่ใช่บนab) นั่น\1คือการอ้างอิงย้อนกลับไปยัง\(.\)กลุ่มการจับภาพนั้นในรูปแบบเดียวกัน

ksh93สนับสนุนการอ้างอิงกลับในรูปแบบ (ตัวอย่างเช่นls -d -- @(?)\1จะแสดงรายการชื่อไฟล์ที่ประกอบด้วยอักขระสองตัวที่เหมือนกัน) ไม่ใช่เชลล์อื่น ๆ BRE มาตรฐานและ PCREs สนับสนุนการอ้างอิงย้อนกลับ แต่ไม่ใช่ ERE มาตรฐานแม้ว่าการปรับใช้ ERE บางอย่างรองรับเป็นส่วนขยาย bash's [[ foo =~ re ]]ใช้ Eres

[[ aa =~ (.)\1 ]]

จะไม่ตรงกัน แต่

re='(.)\1'; [[ aa =~ $re ]]

อาจถ้า ERE ของระบบสนับสนุน


9

คุณต้องการลบทุกอย่างจนถึงแรก␣->␣(ไม่รวม "ลูกศร") และหลังสุด␣/(รวมถึงพื้นที่และเครื่องหมายทับ)

string="Blah: -> r1-ae0-2 / [123]"
string=${string/*->/->}
string=${string/ \/*}

$string-> r1-ae0-2ตอนนี้จะเป็น

เช่นเดียวกับที่สองแทนจะเปิดเข้าไป-> s7-Gi0-0-1:1-US / Foo-> s7-Gi0-0-1:1-US


3

การตอบคำถามนี้เป็นไปไม่ได้โดยไม่ทราบว่ารูปแบบที่แน่นอนทุกข้อความต้องใช้ อย่างไรก็ตามตามแนวทางทั่วไปคุณสามารถพิมพ์ฟิลด์เฉพาะบางอย่างโดยใช้cut:

$ cut -d ' ' -f 2 <<< '-> s7-Gi0-0-1:1-US / Foo'
s7-Gi0-0-1:1-US

หรือคุณสามารถพิมพ์ทุกคอลัมน์ที่ n โดยใช้awk :

$ awk -F' ' '{ for (i=2;i<=NF;i+=4) print $i }' <<< '-> r1-ae0-2 / [123], -> s7-Gi0-0-1:1-US / Foo'
r1-ae0-2
s7-Gi0-0-1:1-US
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.