Có bash hỗ trợ tham chiếu trở lại trong việc mở rộng tham số?


15

Tôi có một biến có tên descrmà có thể chứa một chuỗi Blah: -> r1-ae0-2 / [123], -> s7-Gi0-0-1:1-US / Foovv Tôi muốn nhận được -> r1-ae0-2, -> s7-Gi0-0-1:1-USmột phần từ chuỗi. Hiện tại tôi sử dụng descr=$(grep -oP '\->\s*\S+' <<< "$descr"cho việc này. Có cách nào tốt hơn để làm điều này? Cũng có thể làm điều này với việc mở rộng tham số?

Câu trả lời:


20

ksh93zshcó tham chiếu ngược (hoặc chính xác hơn là 1 , tham chiếu đến các nhóm bắt trong thay thế) ${var/pattern/replacement}, không hỗ trợ 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

( mkshtrang man cũng đề cập rằng các phiên bản trong tương lai sẽ hỗ trợ nó ${KSH_MATCH[1]}cho nhóm chụp đầu tiên. Chưa có sẵn cho đến 2017-04-25).

Tuy nhiên, với bash, bạn có thể làm:

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

Cái nào tốt hơn khi nó kiểm tra rằng mẫu được tìm thấy đầu tiên.

Nếu hỗ trợ regexps của hệ thống của bạn \s/ \S, bạn cũng có thể làm:

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

Với zsh, bạn có thể nhận được toàn bộ sức mạnh của PCRE với:

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

Với zsh -o extendedglob, xem thêm:

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

Di chuyển

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

Nếu có một vài lần xuất hiện của mẫu trong chuỗi, hành vi sẽ thay đổi theo tất cả các giải pháp đó. Tuy nhiên, không ai trong số họ sẽ cung cấp cho bạn một danh sách mới tất cả các kết quả trùng khớp như trong grepgiải pháp dựa trên GNU của bạn .

Để làm điều đó, bạn cần phải thực hiện vòng lặp bằng tay. Chẳng hạn, với bash:

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

Với zsh, bạn có thể sử dụng loại mẹo này để lưu trữ tất cả các trận đấu trong một mảng:

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

1 tham chiếu ngược thường chỉ định một mẫu tham chiếu những gì được khớp bởi một nhóm trước đó. Chẳng hạn, \(.\)\1biểu thức chính quy cơ bản khớp với một ký tự theo sau bởi cùng một ký tự đó (nó khớp với aa, không bật ab). Đó \1là một tham chiếu ngược đến \(.\)nhóm chụp đó trong cùng một mẫu.

ksh93không hỗ trợ các tham chiếu ngược trong các mẫu của nó (ví dụ ls -d -- @(?)\1sẽ liệt kê các tên tệp bao gồm hai ký tự giống nhau), chứ không phải các shell khác. Các BRE và PCRE tiêu chuẩn hỗ trợ các tham chiếu ngược nhưng không phải ERE tiêu chuẩn, mặc dù một số triển khai ERE hỗ trợ nó như một phần mở rộng. bash[[ foo =~ re ]]sử dụng Eres.

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

sẽ không phù hợp, nhưng

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

có thể nếu ERE của hệ thống hỗ trợ nó.


9

Bạn muốn xóa mọi thứ cho đến đầu tiên ␣->␣(không bao gồm "mũi tên") và sau cuối cùng ␣/(bao gồm dấu cách và dấu gạch chéo).

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

$stringbây giờ sẽ có -> r1-ae0-2.

Hai sự thay thế giống nhau sẽ biến -> s7-Gi0-0-1:1-US / Foothành -> s7-Gi0-0-1:1-US.


3

Trả lời dứt khoát này là không thể mà không biết định dạng chính xác mỗi tin nhắn. Tuy nhiên, như một cách tiếp cận chung, bạn có thể in một số trường cụ thể bằng cách sử dụng cut:

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

Hoặc bạn có thể in mọi cột thứ n bằng cách sử dụngawk :

$ 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
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.