文字列から特定の文字を抽出する - Linux シェルスクリプト プログラミング

PROGRAM


文字列から特定の文字を抽出する

Linux シェル (sh) で、文字列から任意の文字を取り出す方法としては、外部コマンド sed を利用する方法があります。

方法としては、sed コマンドの -e オプションを使って正規表現を使用して、必要な文字列に置き換えるという方法をとります。sed に渡す文字列は echo コマンドを使って渡します。

DATA1="IP Address: 192.168.0.1/255.255.255.0

RETVAL=`echo "$DATA1" | sed -e "s/^IP Address:[ \t]\+\([0-9\.]\+\)\/[0-9\.]\+$/\1/g"`

このようにすることで、DATA1 から "192.168.0.1" の部分を切り取って RETVAL 変数に取得することが可能になります。

他にもたとえば、次のような抽出が、sed を使ってできると思います。

# 先頭の 5 文字を取り除く

echo "$DATA1" | sed -e "s/^.\{5\}//"

 

# 末尾の 3 文字を取り除く

echo "$DATA1" | sed -e "s/.\{3\}$//"

値をコロンで分離して、それぞれを NAME 変数と VALUE 変数に格納するといったことも可能です。

NAME=`echo "$DATA1" | sed -e "s/^\([^:]*\):\(.*\)$/\1/"`

VALUE=`echo "$DATA1" | sed -e "s/^\([^:]*\):\(.*\)$/\2/"`

上記の例を、配列を使って行おうと思って試してみたのですけど、コロンで分離した \1 と \2 のどちらかにでもスペースが入ってしまっていると、そこで別の配列要素として分断されてしまってうまくいきませんでした。

 

sed の正規表現について

ところで、sed の引数に指定できる正規表現は、スクリプト言語 Perl のそれとは細かく違っている様子で、思うように動作できなかったので、そのあたりを整理しておこうと思います。

sed の正規表現と Perl の正規表現との差異をまとめると、たとえば、次のような感じになるようでした。

用途 Perl sed
直前の文字をエスケープ \ \
任意の 1 文字(/s が指定された場合は改行文字にもマッチ) . .
[ ] 内の任意の 1 文字にマッチ(右の例では 0 から 9 と a から z) [0-9a-z] [0-9a-z]
[ ] 以外の任意の 1 文字にマッチ(右の例では 0 から 9 と a から z) [^0-9a-z] [^0-9a-z]
数字 1 文字にマッチ \d [0-9]
数字以外の 1 文字にマッチ \D [^0-9]
アルファベットまたは数字 1 文字、アンダーバーにマッチ \w [a-zA-Z_0-9]
アルファベットと数字以外の 1 文字、アンダーバーにマッチ \W [^a-zA-Z_0-9]
空白文字にマッチ \s [ \n\r\f\t]
空白文字以外にマッチ \S [^ \n\r\f\t]
どれかの正規表現にマッチ(右の例では Apple または Blue Berry または Cherry) Apple|Blue Berry|Cherry Apple\|Blue Berry\|Cherry
グループ化 ( ) \( \)
置換時のグループ化された正規表現にマッチした値の取り出し $1 \1
参照から除くグループ化 (?: )  
グループ化された部分の中の左右どちらか | \|
行の先頭にマッチ ^ ^
行の末尾にマッチ $ $
前の文字の 0 個または 1 個にマッチ(右の例では x について) x? x\?
前の文字の 0 個以上にマッチ(右の例では x について) x* x*
前の文字の 1 個以上にマッチ(右の例では x について) x+ x\+
前の文字の m 個にマッチ(右の例では x について) x{m} x\{m\}
前の文字の m 個以上 n 個以下にマッチ(右の例では x について) x{m,n} x\{m,n\}
前の文字の m 個以上にマッチ(右の例では x について) x{m,} x\{m,\}
前の文字の n 個以下にマッチ(右の例では x について) x{,n} x\{,n\}

このように、別のスクリプト言語 Perl とは表記が異なるものがいくつかあるので、コーディングの際には注意が必要そうです。

たとえば、グループ化した部分にマッチした値は、置換の際に "\1" 等の記号で参照することができるのですけど、グループ化を sed では "\(...\)" としなければいけないところを "(...)" としてしまうと、グループ化されていないことになってしまい、"\1" を参照しようとしたときに、次のようなエラーとなってしまいます。

「s」コマンドの右側に無効な\1の参照

invalid reference \1 on `s' command's RHS

もしこのようなエラーが出た場合には、まずは正規表現でのグループ化の記号 \( \) が正しく記載されているかどうかを確認してみると良いかもしれません。

インターネット上の情報では sed が対応していない場合もあるみたいな記載が見られたりしましたけど、もし対応していないとすれば、あまりにも具体的で \1 を想定して作られているようなエラーメッセージなので、対応していないというのも考えにくい気がします。


[ もどる ]