Linux - 正規表現

# 正規表現

正規表現（REGEXP：Regular Expressions）は、特別な文字とテキスト文字から構成されるパターンです。その中には、文字そのものを表すのではなく、制御またはワイルドカードの機能を表すメタ文字が含まれます。ワイルドカードとは異なり、ファイル名を扱うのではなく、テキスト内容を処理するために使用されます。

多くのプログラムや開発言語で広くサポートされています。例えば：vim、less、grep、sed、awk、nginx、mysqlなど。

## 正規表現の分類

- 基本正規表現（BRE: Basic Regular Expressions）
- 拡張正規表現（ERE: Extended Regular Expressions）

## 正規表現エンジン

異なるアルゴリズムを使用して正規表現をチェックおよび処理するソフトウェアモジュール、例えば：PCRE（Perl Compatible Regular Expressions）。

## 正規表現のメタ文字分類

1. 文字のマッチング
2. マッチングする回数
3. 位置のアンカリング
4. グルーピング

ヘルプ情報：`man 7 regex`

### 1.1 基本正規表現のメタ文字

#### 1.1.1 文字のマッチング

```shell
.   # 任意の一文字をマッチングします（\nを除く）
[]  # 指定した範囲にある任意の一文字にマッチング、例：[wang]、[0-9]、[a-z]、[a-zA-Z]
[^] # 指定した範囲外の任意の一文字にマッチング、例：[^wang]
[:alnum:]  # 英数字
[:alpha:]  # 英字大文字小文字、即ち A-Z, a-z
[:lower:]  # 小文字、例：[[:lower:]] は [a-z] と等しい
[:upper:]  # 大文字
[:blank:]  # 空白文字（スペースとタブ）
[:space:]  # スペース、タブ、改行、復帰などの空白を含む、[:blank:]より広い範囲
[:cntrl:]  # 非表示の制御文字（バックスペース、削除、警告音...）
[:digit:]  # 十進数
[:xdigit:] # 十六進数
[:graph:]  # 空白以外の表示文字
[:print:]  # 表示文字
[:punct:]  # 句読点
\s         # 空白文字にマッチング、[\f\n\r\t\v] と等しい、Unicode正規表現では全角スペースにもマッチング
\S         # 非空白文字にマッチング、[^\f\n\r\t\v] と等しい
\w         # 英数字、アンダースコア、漢字などの文字にマッチング、[_[:alnum:]字] と等しい
\W         # 上記以外の文字にマッチング、[^_[:alnum:]字] と等しい
```

#### 例：任意の一文字にマッチング

```shell
[root@rocky86 0722]# grep "r.t" /etc/passwd
operator:x:11:0:operator:/root:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin

[root@rocky86 0722]# grep "r..t" /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
clevis:x:994:988:Clevis Decryption Framework unprivileged 
user:/var/cache/clevis:/sbin/nologin

[root@rocky86 0722]# echo aSSSSb | grep a..b
aSSSb
[root@rocky86 0722]# echo aaaSSSb | grep a..b
aaaSSSb
[root@rocky86 0722]# echo aS-b | grep a..b
aS-b
```

#### 例：範囲マッチング

```shell
[root@centos8 ~]#ls /etc/ | grep 'rc[.0-6]'
rc0.d
rc1.d
rc2.d
rc3.d
rc4.d
rc5.d
rc6.d
rc.d
rc.local

[root@centos8 ~]#ls /etc/ | grep 'rc[.0-6].'
rc0.d
rc1.d
rc2.d
rc3.d
rc4.d
rc5.d
rc6.d
rc.d
rc.local

[root@centos8 ~]#ls /etc/ | grep 'rc[.0-6]\.'
rc0.d
rc1.d
rc2.d
rc3.d
rc4.d
rc5.d
rc6.d

[root@rocky86 0722]# echo ma | grep [lmn]
ma

[root@rocky86 0722]# echo ma | grep [^lmn]
ma

[root@rocky86 0722]# echo "ab1c2d3e4444fABCg$%^" | grep "[0-Z]"
ab1c2d3e4444fABCg$%^

[root@rocky86 0722]# echo "ab1c2d3e4444fABCg$%^" | grep "[0-Z]"
ab1c2d3e4444fABCg$%^

[root@rocky86 0722]# echo "ab1c2d3e4444fABCg$%^" | grep "[0-9]"
ab1c2d3e4444fABCg$%^

[root@rocky86 0722]# echo "ab1c2d3e4444fABCg$%^" | grep "[a-z]"
ab1c2d3e4444fABCg$%^

[root@rocky86 0722]# echo "ab1c2d3e4444fABCg$%^" | grep "[A-Z]"
ab1c2d3e4444fABCg$%^

[root@rocky86 0722]# echo "ab1c2d3e4444fABCg$%^" | grep "[^0-Z]"
ab1c2d3e4444fABCg$%^

[root@rocky86 0722]# echo "ab1c2d3e4444fABCg$%^" | grep "[[:punct:]]"
ab1c2d3e4444fABCg$%^

[root@rocky86 0722]# echo "1 2 3" | grep "[[:blank:]]"
1 2  3

[root@rocky86 0722]# echo "1 2 3" | grep "[^[:blank:]]"
1 2  3
```

### 1.1.2 マッチングする回数

特定の文字の後ろにつけて、その文字が出現する回数を指定する

```shell
*    # 前の文字が0回以上の任意の回数マッチング、貪欲模式：できるだけ長くマッチング
.*   # 任意の長さの任意の文字
\?   # 前の文字が0回または1回出現、つまり存在するかどうか
\+   # 前の文字が少なくとも1回出現
\{n\}    # 前の文字がn回出現
\{m,n\}  # 前の文字が少なくともm回、最大でn回出現
\{,n\}   # 前の文字が最大でn回出現、<=n
\{n,\}   # 前の文字が少なくともn回出現
```

#### 例：

```shell
[root@rocky86 ~]# echo "rt" | grep "ro*t"
rt

[root@rocky86 ~]# echo "rot" | grep "ro*t"
rot

[root@rocky86 ~]# echo "root" | grep "ro*t"
root

[root@rocky86 ~]# echo "roooooooot" | grep "ro*t"
roooooooot
```

#### 例：任意の長さの任意の文字

```shell
[root@rocky86 ~]# grep "r.*t" /etc/passwd
```

#### 例：0次または1次

```shell
[root@centos8 ~]# echo /etc/ |grep "/etc/\?"
/etc/

[root@centos8 ~]# echo /etc |grep "/etc/\?"
/etc
```

#### 例：1回または複数回

```shell
[root@rocky86 ~]# echo google | grep "go\+gle"
google

[root@rocky86 ~]# echo gogle | grep "go\+gle"
gogle

[root@rocky86 ~]# echo ggle | grep "go\+gle"
[root@rocky86 ~]# echo gooogle | grep "go\+gle"
gooogle
```

#### 例：カスタム回数

```shell
#2回
[root@rocky86 ~]# echo google | grep "go\{2\}gle"
google

#2回到5回
[root@rocky86 ~]# echo google | grep "go\{2,5\}gle"
google

#2回到多回
[root@rocky86 ~]# echo google | grep "go\{2,\}gle"
google

#0回到2回
[root@rocky86 ~]# echo google | grep "go\{,2\}gle"
google
```

#### 例：正負数をマッチングする

```shell
[root@centos8 ~]#echo -1 -2 123 -123 234 |grep '-\?[0-9]\+'
grep: invalid option -- '\'
Usage: grep [OPTION]... PATTERN [FILE]...
Try 'grep --help' for more information.

[root@centos8 ~]#echo -1 -2 123 -123 234 |grep '\-\?[0-9]\+'
-1 -2 123 -123 234

[root@centos8 ~]#echo -1 -2 123 -123 234 |grep -E '-?[0-9]+'
grep: invalid option -- '?'
Usage: grep [OPTION]... PATTERN [FILE]...
Try 'grep --help' for more information.

[root@centos8 ~]#echo -1 -2 123 -123 234 |grep -E '\-?[0-9]+'
-1 -2 123 -123 234

[root@centos8 ~]#echo -1 -2 123 -123 234 |grep -E -- '-?[0-9]+'
-1 -2 123 -123 234

[root@centos8 ~]#echo -1 -2 123 -123 234 |grep -E '(-)?[0-9]+'
-1 -2 123 -123 234

```

### IPアドレスの例

```shell
[root@centos8 ~]#ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
       inet 10.0.0.8 netmask 255.255.255.0 broadcast 10.0.0.255
       inet6 fe80::20c:29ff:fee1:e53 prefixlen 64 scopeid 0x20<link>
       ether 00:0c:29:e1:0e:53 txqueuelen 1000 (Ethernet)
       RX packets 45953 bytes 21739254 (20.7 MiB)
       RX errors 0 dropped 0 overruns 0 frame 0
       TX packets 35886 bytes 26575579 (25.3 MiB)
       TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

[root@centos8 ~]#ifconfig eth0|grep netmask |grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}'|head -n1
10.0.0.8

[root@centos8 ~]#ifconfig eth0|grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}'|head -n1
10.0.0.8
```

### 1.1.3 位置アンカー

位置アンカー（位置锚定）を使用して、特定の位置にマッチさせることができます。

```shell
^       # 行の先頭にマッチ。パターンの最も左側で使用されます。
$       # 行の末尾にマッチ。パターンの最も右側で使用されます。
^PATTERN$ # 完全な行にマッチするためのパターン
^$      # 空行にマッチ
^[[:space:]]*$ # 空白行にマッチ
\< または \b     # 単語の始まりにマッチ。単語パターンの左側で使用されます。
\> または \b    # 単語の終わりにマッチ。単語パターンの右側で使用されます。
\<PATTERN\>    # 完全な単語にマッチ
# 注意: 単語は文字、数字、アンダースコアで構成されます。
```

#### 例：

```shell
# # で始まる行
grep "^#" /etc/fstab

# bash で終わる行
grep "bash$" /etc/passwd

# すべての空行
grep "^$" /etc/profile

# すべての非空行
grep -v "^$" /etc/profile

# すべての非コメント行
grep "^[^#]" /etc/profile

# すべての空行およびコメント行を除外
grep -v "^$" /etc/profile | grep -v "^#"

# 同上
grep -v -e "^$" -e "^#" /etc/profile

# 同上
grep -v '^$\|^#' /etc/profile

# 単語にマッチ
echo mage | grep "\<mage\>"
echo mage- | grep "\<mage\>"
echo magee | grep "\<mage\>"
```

#### 例：

```shell
# 非コメント行を表示
[root@centos8 ~]#grep '^[^#]' /etc/fstab
UUID=acf9bd1f-caae-4e28-87be-e53afec61347 /               xfs   defaults   
0 0
UUID=1770b87e-db5a-445e-bff1-1653ac64b3d6 /boot           ext4   defaults       
1 2
UUID=ffffd919-d674-44d9-a4e7-402874f0a1f0 /data           xfs   defaults       
0 0
UUID=409e36d2-ac5e-423f-ad78-9b12db4576bd swap             swap   defaults       
0 0
```

### 1.1.4 グループ化その他

#### 3.1.4.1 グループ化

```shell
# グループ化：() 複数の文字を一つの単位として扱い、例：(root)+
# 後方参照：グループ括弧内のパターンにマッチした内容は、正規表現エンジンによって内部変数に保存されます。これらの変数は \1, \2, \3, ... と名付けられます。
# \1 は、左側から数えて最初の左括弧とそれに対応する右括弧の間のパターンにマッチした文字列を指します。
# 注意：\0 は正規表現にマッチしたすべての文字列を表します。
```

#### 例：

```shell
# ab の後に c が3回出現
echo abccc | grep "abc\{3\}"

# abc が一つの単位として3回出現
echo abcabcabc | grep "\(abc\)\{3\}"

# 後方参照
echo abcabcabc | grep "\(abc\)\1"
echo abcabcabc | grep "\(abc\)\1\1"

# \1 は最初のグループの内容、すなわち abc を参照
echo "abcdefabc" | grep "\(abc\)def\1"

# \1 は最初のグループの内容、すなわち abc を参照し、\{3\} はその内容が3回出現することを表す
echo abc-def-abcabcabc | grep "^\(abc\)-\(def\)-\1\{3\}"

[root@rocky86 ~]# echo abc-def-abcabcabc-def-abc-defdef | grep "^\(abc\)-\(def\)-\1\{3\}-\2-\1-\2\{2\}"
abc-def-abcabcabc-def-abc-defdef
```

#### IP を取得する例：

```shell
# IP アドレスを抽出
[root@rocky86 ~]# ifconfig ens160 | grep -o "\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}" | head -n 1
10.0.0.158
```

#### 注意：

後方参照は、先にあるグループ括弧内のパターンにマッチした文字列を参照するもので、パターンそのものではありません。

#### 1.1.4.2 または（OR）
または：|
```shell
# a または b
[root@rocky86 ~]# echo "a" | grep "a\|b"
a
[root@rocky86 ~]# echo "b" | grep "a\|b"
b
[root@rocky86 ~]# echo "ab" | grep "a\|b"
ab
[root@rocky86 ~]# echo "abc" | grep "a\|b"
abc
[root@rocky86 ~]# echo "cab" | grep "a\|b"
cab
[root@rocky86 ~]# echo "acb" | grep "a\|b"
acb

```

#### 例：

```shell
# 12a または b にマッチ
[root@rocky86 ~]# echo "12a" | grep "12a\|b"
12a
[root@rocky86 ~]# echo "b" | grep "12a\|b"
b
[root@rocky86 ~]# echo "12ab" | grep "12a\|b"
12ab
[root@rocky86 ~]# echo "12ba" | grep "12a\|b"
12ba
#12a または 12b   12a\|12b
[root@rocky86 ~]# echo "12a" | grep "12a\|12b"
12a
[root@rocky86 ~]# echo "12b" | grep "12a\|12b"
12b
[root@rocky86 ~]# echo "12ab" | grep "12a\|12b"
12ab
[root@rocky86 ~]# echo "12ba" | grep "12a\|12b"
12ba
#12a または 12b   12\(a\|b\)
[root@rocky86 ~]# echo "12a" | grep "12\(a\|b\)"
12a
[root@rocky86 ~]# echo "12b" | grep "12\(a\|b\)"
12b
[root@rocky86 ~]# echo "12ab" | grep "12\(a\|b\)"
12ab
[root@rocky86 ~]# echo "12ba" | grep "12\(a\|b\)"
12ba
#12a または 12b   12[ab]
[root@rocky86 ~]# echo "12a" | grep "12[ab]"
12a
[root@rocky86 ~]# echo "12b" | grep "12[ab]"
12b
[root@rocky86 ~]# echo "12ab" | grep "12[ab]"
12ab
[root@rocky86 ~]# echo "12ba" | grep "12[ab]"
12ba

```

#### 空行と # で始まる行を除外する例：

```shell
[root@centos6 ~]#grep -v '^#' /etc/httpd/conf/httpd.conf |grep -v ^$
[root@centos6 ~]#grep -v '^#\|^$' /etc/httpd/conf/httpd.conf
[root@centos6 ~]#grep -v '^\(#\|$\)' /etc/httpd/conf/httpd.conf 
[root@centos6 ~]#grep "^[^#]" /etc/httpd/conf/httpd.conf
```
### 1.2 拡張正規表現のメタ文字

#### 1.2.1 文字の一致
- `.` # 任意の単一文字に一致します。
- `[wang]` # 指定された範囲のいずれかの文字に一致します。
- `[^wang]` # 指定された範囲外の任意の文字に一致します。
- `[:alnum:]` # 英数字（アルファベットと数字）に一致します。
- `[:alpha:]` # 任意の英字（大文字と小文字のA-Z, a-z）に一致します。
- `[:lower:]` # 小文字に一致します。例: `[[:lower:]]` は `[a-z]` に相当します。
- `[:upper:]` # 大文字に一致します。
- `[:blank:]` # 空白文字（スペースとタブ）に一致します。
- `[:space:]` # 水平および垂直の空白文字に一致します（`[:blank:]`よりも範囲が広い）。
- `[:cntrl:]` # 非表示の制御文字（バックスペース、削除、ベルなど）に一致します。
- `[:digit:]` # 十進数数字に一致します。
- `[:xdigit:]` # 16進数数字に一致します。
- `[:graph:]` # 非空白の表示可能文字に一致します。
- `[:print:]` # 表示可能な文字に一致します。
- `[:punct:]` # 句読点に一致します。

#### 1.2.2 一致回数
- `*` # 直前の文字が0回以上出現する場合に一致します。
- `?` # 直前の文字が0回または1回出現する場合に一致します。
- `+` # 直前の文字が1回以上出現する場合に一致します。
- `{n}` # 直前の文字がn回出現する場合に一致します。
- `{m,n}` # 直前の文字が少なくともm回、多くてもn回出現する場合に一致します。

#### 1.2.3 位置アンカー
- `^` # 行の先頭に一致します。
- `$` # 行の末尾に一致します。
- `\<`, `\b` # 単語の開始部分に一致します。
- `\>`, `\b` # 単語の終了部分に一致します。

#### 3.2.4 その他のグループ化
- `()` # グループ化に使用されます。後方参照の際に `1`, `2`, ... という形で使用されます。注意: `\0` は正規表現に一致するすべての文字列を表します。
- `|` # OR条件を表します。例: `a|b` は「aまたはb」に一致します。
- `C|cat` # 「Cまたはcat」に一致します。
- `(C|c)at` # 「Catまたはcat」に一致します。

#### 例:
```shell
# 初期設定のIPアドレスを取得
[root@centos8 ~]#ifconfig | grep -Ewo "(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])"|head -n1
10.0.0.8
```