sed と awk （３）Regular Expression レンジとQuantifier 

# レンジ
正規表現の範囲の表現方法を学ぶ

## `[A-Za-z]`
任意の文字

## `[0-9]`
任意の数字

## `[a-z_]`
小文字の英字もしくは、アンダースコア

## `[349]`
3, 4, もしくは 9 にマッチする

## サンプル

シェルの中で定義されている関数の一覧を取得する。`declare -f` そして、grep で先頭が小文字もしくはアンダースコアのものを抽出する。

```bash
$ declare -f | grep '^[a-z_]'
__expand_tilde_by_ref ()
__get_cword_at_cursor_by_ref ()
__git_eread ()
   :
_xfunc ()
_xinetd_services ()
command_not_found_handle ()
dequote ()
quote ()
quote_readline ()
```

`logroate.d`が4もしくは6で終わる行を検索する。`$`は行末の意味

```bash
 grep 'rotate [46]$' /etc/logrotate.d/*
/etc/logrotate.d/rsyslog:	rotate 4
/etc/logrotate.d/ufw:	rotate 4
/etc/logrotate.d/unattended-upgrades:  rotate 6
/etc/logrotate.d/waagent.logrotate:    rotate 6
```

ちなみに、レンジの中で`^` を使うとnegateつまり反対の意味になるようだ。この例だと`rotate ...`で終わりが４以外

```bash
/etc/logrotate.d/apport:       rotate 7
/etc/logrotate.d/lxd:       rotate 7
/etc/logrotate.d/rsyslog:	rotate 7
/etc/logrotate.d/unattended-upgrades:  rotate 6
/etc/logrotate.d/waagent.logrotate:    rotate 6
```

Serverだけど、先頭が大文字でも小文字でもヒットさせる

```bash
$ grep '[Ss]erver' /etc/ntp.conf
# Specify one or more NTP servers.
# Use servers from the NTP Pool Project. Approved by Ubuntu Technical Board
# Use Ubuntu's ntp server as a fallback.
# Note that "restrict" applies to both servers and clients, so a configuration
# up blocking replies from your own upstream servers.
# Local users may interrogate the ntp server more closely.
#server 127.127.8.1 mode 135 prefer    # Meinberg GPS167 with PPS
#server 127.127.22.1                   # ATOM(PPS)
```

# 文字のバウンダリ

## `\s` 
ホワイトスペース

`\ssystem` は、例えば`file system` にマッチする

## `\b` 
文字の境界線。明確な仕様は、システムによって違うらしいが、例えば、空白とかハイフンとか。試してみると結構ヒットする。

```bash
$ cat ./words
 word
word
-word-
(word)
{word}
[word]
$ grep '\bword\b' ./words
 word
word
-word-
(word)
{word}
[word]
```

実際にやってみる。区切り文字のあるものにヒット

```bash
$ grep 'server\b'  /etc/ntp.conf
# Use Ubuntu's ntp server as a fallback.
# Local users may interrogate the ntp server more closely.
#server 127.127.8.1 mode 135 prefer    # Meinberg GPS167 with PPS
#server 127.127.22.1                   # ATOM(PPS)
```

`\s`で空白に限定してヒット

```bash
$ grep 'server\s'  /etc/ntp.conf
# Use Ubuntu's ntp server as a fallback.
# Local users may interrogate the ntp server more closely.
#server 127.127.8.1 mode 135 prefer    # Meinberg GPS167 with PPS
#server 127.127.22.1                   # ATOM(PPS)
```

`\S` で複数形にヒット。ちなみに、`\S` は非空白文字。

* [正規表現](http://doc.okkez.net/static/1.8.7/doc/spec=2fregexp.html)

```bash
$ grep 'server\S'  /etc/ntp.conf
# Specify one or more NTP servers.
# Use servers from the NTP Pool Project. Approved by Ubuntu Technical Board
# Note that "restrict" applies to both servers and clients, so a configuration
# up blocking replies from your own upstream servers.
```

pop を検索する。

```bash
$ grep pop /etc/services
pop2		109/tcp		postoffice pop-2 # POP version 2
pop2		109/udp		pop-2
pop3		110/tcp		pop-3		# POP version 3
pop3		110/udp		pop-3
pop3s		995/tcp				# POP-3 over SSL
pop3s		995/udp
kpop		1109/tcp			# Pop with Kerberos
poppassd	106/tcp				# Eudora
poppassd	106/udp
```

pop+数字で、前後がワードの区切り

```bash
$ grep '\bpop[0-9]\b' /etc/services
pop2		109/tcp		postoffice pop-2 # POP version 2
pop2		109/udp		pop-2
pop3		110/tcp		pop-3		# POP version 3
pop3		110/udp		pop-3
```

ほぼ同じだけど、pop+数字の前はワード区切りの文字では無い（そんな場所は無いのtでヒットしない）

```bash
$ grep '\Bpop[0-9]\b' /etc/services
```

最終文字のあとが、ワード区切り文字では無い

```bash
$ grep 'pop[0-9]\B' /etc/services
pop3s		995/tcp				# POP-3 over SSL
pop3s		995/udp
```

# Quantifiers 

## '*'

`u*` は、uが０以上出現する事にマッチ

## '?'

`u?' は、uが０もしくは、１回のみ出現にマッチ

## '+'

'u+' は、uが１以上出現する事にマッチ

## '{n}`

'u{3}` は `uuu` にマッチする

## サンプル

とても良いサンプルだったので、そのまま。

_test_

```bash
                      #start
start end
  start  end
  startend
  b
 #bb
bbb
#end
```

これを元に正規表現をテストします。

### start のマッチ

```bash
$ grep 'start' ./test
                      #start
start end
  start  end
  startend
```

### 'start end' のマッチ

```bash
$ grep 'start end' ./test
start end
```

### 'start' と 'end' の間に０以上複数のスペースとマッチ

```bash
 grep 'start\s*end' test
start end
  start  end
  startend
```

### 'start' と 'end' の間に一つの空白とマッチ

のはずが出ない。

```bash
$ grep 'start\s?end' test
$
```

これは、grep が標準で拡張の構文に対応していないため。`-E` オプションをつけると対応される。これで１文字もしくは、０文字の空白のマッチが確認できる。

```bash
$ grep -E 'start\s?end' test
start end
  startend
```

具体的な繰り返し回数のマッチも次の通り。

```bash
$ grep -E 'start\s{2}end' test
  start  end
```

ファイルを改造して、追加する。

_test_

```bash
                      #start
start end
  start  end
  startend
  b
 #bb
bbb
#end
ab12 7af
a234 7ef
```

もう少し複雑なパターンを試してみる。英字二文字、そのあと、数字が一文字、もしくは二文字というパターンマッチ

```bash
$ grep -E '[a-z]{2}[0-9]{1,2}' test
ab12 7af
```

うん。正規表現をは強力やな。

# サンプル

ソーシャルセキュリティナンバーを検索する


_employee_

```
Tsuyoshi,Ushio, 232-78-3456
Michael,Jackson,,
Foo,Bar,xxx-xx-xxxx
Hello,World,1879-0
Elton,Jhon,345-23-2313
Muddy,Waters,563-23-8137
Steve,Vai,,
```

最初に、`grep -vE` を指定して検索。`-v` はマッチを反転するので、ヒットしないものが対象になる。ソーシャルセキュリティナンバーは、`999-99-9999`的なフォーマットなので次のようになる。

```bash
$ grep -vE '\b[0-9]{3}-[0-9]{2}-[0-9]{4}\b' employees
Michael,Jackson,,
Foo,Bar,xxx-xx-xxxx
Hello,World,1879-0
Steve,Vai,,
```

ちゃんとマッチさせるには、`-v`をとる

```bash
$ grep -E '\b[0-9]{3}-[0-9]{2}-[0-9]{4}\b' employees
Tsuyoshi,Ushio, 232-78-3456
Elton,Jhon,345-23-2313
Muddy,Waters,563-23-8137
```

ついでだからドライバーズライセンスもやってみよう。New numbers will begin with “WDL” followed by a string of nine randomly assigned letters or numbers.なので簡単そう。

```bash
$ cat drivers
Tsuoyshi,Ushio, WDL23N6BG23B
Yamada,Taro, XXXXXXXXXXXX
Sanda,Yu,,
Takeru,Ushio, WDL15NB6G22C
```
うん。簡単だった。

```bash
$ grep -E '\bWDL[0-9,A-Z]{9}\b' drivers
Tsuoyshi,Ushio, WDL23N6BG23B
Takeru,Ushio, WDL15NB6G22C
```