web-dev-qa-db-ja.com

bashはパラメーター展開で後方参照をサポートしていますか?

文字列Blah: -> r1-ae0-2 / [123]-> s7-Gi0-0-1:1-US / Fooなどを含むことができるdescrという名前の変数があります。文字列から-> r1-ae0-2-> s7-Gi0-0-1:1-USの部分を取得したい。現時点では、descr=$(grep -oP '\->\s*\S+' <<< "$descr"を使用しています。これを行うより良い方法はありますか?パラメータ展開でこれを行うことも可能ですか?

18
Martin

_ksh93_とzshには後方参照があります(より正確には1、置換でのキャプチャグループへの参照)bashではなく_${var/pattern/replacement}_内のサポート。

_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
_

mksh manページには、将来のバージョンで最初のキャプチャグループの_${KSH_MATCH[1]}_がサポートされることも記載されています。2017年4月25日現在、まだ使用できません)。

ただし、bashを使用すると、次のことができます。

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

最初にパターンが見つかったことを確認するので、どちらが良いでしょう。

システムの正規表現が_\s_/_\S_をサポートしている場合は、次のこともできます。

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

zshを使用すると、PCREの能力を最大限に引き出すことができます。

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

ポータブル:

_$ 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は2つの同一の文字で構成されるファイル名をリストします)。標準のBREとPCREは後方参照をサポートしていますが、標準のEREはサポートしていませんが、一部のERE実装は拡張としてサポートしています。 bashの_[[ foo =~ re ]]_はEREを使用します。

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

一致しませんが、

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

システムのEREがサポートしている場合があります。

21

最初の␣->␣(「矢印」を含まない)まで、および最後の␣/の後(スペースとスラッシュを含む)までのすべてを削除します。

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

$string-> r1-ae0-2になります。

同じ2つの置換は、-> s7-Gi0-0-1:1-US / Foo-> s7-Gi0-0-1:1-USに変換します。

9
Kusalananda

everyメッセージが受け取る正確な形式を知らなければ、これに明確に答えることは不可能です。ただし、一般的なアプローチとして、cutを使用して特定のフィールドを印刷できます。

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

または awkを使用してn列ごとに出力

$ 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
3
l0b0