正規表現を使用してbashで検索して置換する


160

私はこの例を見ました:

hello=ho02123ware38384you443d34o3434ingtod38384day
echo ${hello//[0-9]/}

これはこの構文に従います: ${variable//pattern/replacement}

残念ながら、patternフィールドは完全な正規表現構文をサポートしていないようです(たとえば、.またはを使用\sすると、リテラル文字と一致しようとします)。

完全な正規表現構文を使用して文字列を検索/置換するにはどうすればよいですか?


:ここに関連する質問が見つかりstackoverflow.com/questions/5658085/...
jheddings

2
FYI \sは、標準のPOSIX定義の正規表現構文(BREでもERE でもありません)の一部ではありません。これはPCRE拡張機能であり、シェルからはほとんど利用できません。[[:space:]]より普遍的な同等物です。
Charles Duffy 14

1
\s置き換えることができる[[:space:]]方法によって、.によって?、ベースラインシェルパターン言語へextglob拡張はオプションのサブグループ、繰り返し基などのようなもののために使用することができます。
Charles Duffy、


Solarisのbashバージョン4.1.11でこれを使用しています... echo $ {hello // [0-9]}最後のスラッシュがないことに注意してください。
ダニエル・リストン2018

回答:


175

sedを使用:

MYVAR=ho02123ware38384you443d34o3434ingtod38384day
echo "$MYVAR" | sed -e 's/[a-zA-Z]/X/g' -e 's/[0-9]/N/g'
# prints XXNNNNNXXXXNNNNNXXXNNNXNNXNNNNXXXXXXNNNNNXXX

以降-eのは順番に処理されることに注意してください。また、式のgフラグは入力のすべての出現に一致します。

この方法を使用してお気に入りのツール、つまりperl、awkを選択することもできます。例:

echo "$MYVAR" | perl -pe 's/[a-zA-Z]/X/g and s/[0-9]/N/g'

これにより、さらにクリエイティブな一致を行うことができます...たとえば、上記の切り取りでは、最初の式に一致がない場合(遅延and評価のため)に数値の置換は使用されません。そしてもちろん、あなたはあなたの入札を行うためにPerlの完全な言語サポートを持っています...


これは、私が知る限り、1つの置換のみを行います。私が投稿したコードのように、パターンのすべての出現箇所を置き換える方法はありますか?
Lanaru、2012年

複数の置換とグローバルパターンマッチングを示すために私の回答を更新しました。それが役立つかどうか私に知らせてください。
jheddings 2012年

本当にありがとう!好奇心から、なぜ(元の回答の)1行バージョンから2行バージョンに切り替えたのですか?
Lanaru、2012年

9
使用sedまたはその他の外部ツールは、デュー・プロセスの初期化時に高価です。特にbash置換を使用するとsed、ループ内の各項目を呼び出すよりも3倍以上速くなることがわかったため、すべてbashソリューションを検索しました。
rr- 2014年

6
@CiroSantilli六四事件法轮功纳米比亚威视、当然のことですが、それは賢明なことではありません。はい、bashは何があっても遅くなりますが、サブシェルを回避する適切に記述されたbashは、小さな小さなタスクごとに外部ツールを呼び出すbashよりも桁違いに高速です。また、適切に記述されたシェルスクリプトは、高速なインタープリター(awkと同等のパフォーマンスを持つksh93など)の恩恵を受けますが、不十分に記述されたものは、何もする必要がありません。
Charles Duffy

133

これは実際に純粋なbashで行うことできます:

hello=ho02123ware38384you443d34o3434ingtod38384day
re='(.*)[0-9]+(.*)'
while [[ $hello =~ $re ]]; do
  hello=${BASH_REMATCH[1]}${BASH_REMATCH[2]}
done
echo "$hello"

...収量...

howareyoudoingtodday

2
何かがあなたをこれらが好きになると教えてくれます:stackoverflow.com/questions/5624969/… =)
nickl-

=~キーです。しかし、ループでの再割り当てを考えると、少し不格好です。2年前の@jheddingsソリューションは、もう1つの優れたオプションです(sedまたはperlを呼び出す)。
ブレントファウスト

3
各呼び出しを使用して複数行の入力を処理する場合は、sedまたはを呼び出すのperlが賢明です。ループを使用して出力ストリームを処理するのではなく、ループの内側でこのようなツールを呼び出すのは、簡単です。
Charles Duffy

2
ちなみに、zshでは、の$match代わりです$BASH_REMATCH。(でbashのように動作させることができsetopt bash_rematchます。)
Marian

それは奇妙です-zshがPOSIXシェルになることを試みていないので、それは間違いなくPOSIX指定(シェルまたはシステム関連)の目的で使用されるすべて大文字の変数と予約されている小文字の変数に関するPOSIXガイダンスの手紙に従っていますアプリケーションの使用。しかし、zshはアプリケーション自体ではなくアプリケーションを実行するものであるため、システムの名前空間ではなくアプリケーション変数の名前空間を使用するというこの決定は、ひどく混乱しているようです。
Charles Duffy

94

これらの例は、sedを使用する必要なく、bashでも機能します。

#!/bin/bash
MYVAR=ho02123ware38384you443d34o3434ingtod38384day
MYVAR=${MYVAR//[a-zA-Z]/X} 
echo ${MYVAR//[0-9]/N}

文字クラスの括弧式を使用することもできます

#!/bin/bash
MYVAR=ho02123ware38384you443d34o3434ingtod38384day
MYVAR=${MYVAR//[[:alpha:]]/X} 
echo ${MYVAR//[[:digit:]]/N}

出力

XXNNNNNXXXXNNNNNXXXNNNXNNXNNNNXXXXXXNNNNNXXX

@Lanaruが知りたいのは、質問を正しく理解した場合、「フル」またはPCRE拡張機能\s\S\w\W\d\Dなどがphp ruby​​ pythonなどでサポートされているように機能しない理由です。これらの拡張機能は、Perl互換の正規表現(PCRE)およびシェルベースの正規表現の他の形式と互換性がない可能性があります。

これらは機能しません:

#!/bin/bash
hello=ho02123ware38384you443d34o3434ingtod38384day
echo ${hello//\d/}


#!/bin/bash
hello=ho02123ware38384you443d34o3434ingtod38384day
echo $hello | sed 's/\d//g'

すべてのリテラル「d」文字を削除した出力

ho02123ware38384you44334o3434ingto38384ay

しかし、以下は期待どおりに機能します

#!/bin/bash
hello=ho02123ware38384you443d34o3434ingtod38384day
echo $hello | perl -pe 's/\d//g'

出力

howareyoudoingtodday

もう少しわかりやすくなりますが、まだ混乱していない場合は、REG_ENHANCEDフラグが有効になっているMac OS Xで試してみてください。

#!/bin/bash
MYVAR=ho02123ware38384you443d34o3434ingtod38384day;
echo $MYVAR | grep -o -E '\d'

* nixのほとんどのフレーバーでは、次の出力のみが表示されます。

d
d
d

nJoy!


6
恩赦?POSIX.2 BREまたはERE構文で${foo//$bar/$baz}はありません -これはfnmatch()スタイルのパターンマッチングです。
Charles Duffy

8
...そう、一方${hello//[[:digit:]]/}作品、我々は唯一の文字が先行桁フィルタリングしたい場合はo${hello//o[[:digit:]]*}のfnmatchのパターンで、以来、予想1(とは全く異なる振る舞いを持つことになり*、むしろする直前の項目を修正するよりも、すべての文字にマッチします0以上)。
Charles Duffy

1
fnmatchの完全な仕様については、pubs.opengroup.org / onlinepubs / 9699919799 / utilities /…(および参照により組み込まれているすべてのもの)を参照してください。
Charles Duffy

1
man bash:追加の2項演算子=〜が利用可能で、==および!=と同じ優先順位があります。これを使用すると、演算子の右側の文字列は拡張正規表現と見なされ、それに応じて一致します(regex(3)のように)。
nickl- 2014年

1
@aderchox正解、使用できる数字[0-9]または[[:digit:]]
nickl-

13

繰り返し呼び出しを行い、パフォーマンスに関心がある場合、このテストは、BASHメソッドがsedへの分岐や他の外部プロセスの可能性よりも約15倍速いことを明らかにします。

hello=123456789X123456789X123456789X123456789X123456789X123456789X123456789X123456789X123456789X123456789X123456789X

P1=$(date +%s)

for i in {1..10000}
do
   echo $hello | sed s/X//g > /dev/null
done

P2=$(date +%s)
echo $[$P2-$P1]

for i in {1..10000}
do
   echo ${hello//X/} > /dev/null
done

P3=$(date +%s)
echo $[$P3-$P2]

1
あなたはフォークを低減するための方法に興味があれば、単語を検索newConnectorにこの答えbashでコマンドの出力に変数を設定する方法?
F.ハウリ

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.