【sed / awk / grep】文字列の置換・抽出・検索と正規表現 | Linux Cheat Sheet

# 1. はじめに
`sed` `awk` `grep` コマンドの意味は、

- `sed`：文字列を置換
- `awk`：文字列を抽出
- `grep`：文字列を検索

です。

# 2. sedコマンド

## 2-1. 文字列を置換

```
sed -e "s/置換元/置換後/g" ファイル名
```

- `-e`オプション：指定したスクリプト（条件式）で変換処理を行う
- `s`：冒頭のsは「置換元を置換後に変換する」を表す
- `g`：末尾のgは「条件を満たす"すべての"文字列を置換する」を表す
- `置換元`：正規表現で記述可
- `置換後`：`\1`や`\2`で、置換元でマッチした文字を取ってこれる

## 2-2. 正規表現

正規表現は、下記3種類のパーツを組み合わせて使います。

- 文字を指定するパーツ：「A」「1」「.」など
- 文字数を指定するパーツ：「*」「+」「{1,3}」など
- 文字の位置を指定するパーツ：「^」「$」など

### ▼文字を指定するパーツ

|正規表現|意味|
|:--|:--|
|`A` `1` `a`など|具体文字を指定|
|`.`|何らかの１文字|
|`[AHZ]`|どれかの文字に一致する|
|`[0-9]` `\d`|何らかの半角数字|
|`[a-z]`|何らかの小文字の半角英字|
|`[A-Z]`|何らかの大文字の半角英字|
|`[0-9a-zA-Z]` `\w`|何らかの半角英数字|
|`\s`|改行文字を含んだ空白文字|

### ▼文字数を指定するパーツ
|正規表現|意味|
|:--|:--|
|`*`|0回以上の繰り返し|
|`+`|1回以上の繰り返し|
|`?`|0回または1回の繰り返し|
|`{1,3}`|1〜3回の繰り返し|
|`{3,}`|3回以上の繰り返し|
|`{,8}`|8回以内の繰り返し|
|`{6}`|6回ちょうどの繰り返し|

- １文字の繰り返しではなく複数文字列の繰り返しのパターンでは、複数文字列を( )で囲む。
- Linuxコマンドの場合は、( )文字は、制御文字の意味を持つため、\でエスケープする必要がある。
- Linuxコマンドの場合は、{ }文字は、( )文字同様に制御文字の意味を持つため、\でエスケープする必要がある。

### ▼文字の位置を指定するパーツ
|正規表現|意味|
|:--|:--|
|`^`|行頭を表す|
|`$`|行末を表す|

## 2-3. 使用例
```
# test.csvの最初の5行を出力

$ cat test.csv | head -n 5
"impression_id","user_id"
"SCn0v02UpTCUw19c","L61g2LrwdRV4Q9Eg"
"2sM5au8OrxZ10CSO","DqZIe64440WnwdZY"
"1WtRtO2G0ulE9n0P","5XsQ09NKpT0b4gTx"
"gZq9GLs1l2jEN11Y","u69ErH70f7PitXQA"
```

- `cat`：引数（ファイルor標準入力）を標準出力する
- `|`（パイプ）：直前の実行結果を直後の引数に渡す
- `head`：引数（ファイルor標準入力）の先頭n行を抜き出すコマンド
  - `-n`オプション：行数を指定

```
# test.csvからuser_idを抽出

$ cat test.csv | sed -e "s/^\"[0-9a-zA-Z_]*\",\"\([0-9a-zA-Z_]*\)\"/\1/g" | head -n 5
user_id
L61g2LrwdRV4Q9Eg
DqZIe64440WnwdZY
5XsQ09NKpT0b4gTx
u69ErH70f7PitXQA
```

```
# 1行目の「user_id」をgrepコマンドで除外

$ cat test.csv | grep -v "user_id" | sed -e "s/^\"[0-9a-zA-Z_]*\",\"\([0-9a-zA-Z_]*\)\"/\1/g" | head -n 5
L61g2LrwdRV4Q9Eg
DqZIe64440WnwdZY
5XsQ09NKpT0b4gTx
u69ErH70f7PitXQA
SploAGrHQemMB9tb
```

- `grep`コマンド：文字列検索
- `-v`オプション：パターンにマッチした行以外を出力する

【参考サイト】
▼sedチートシート
https://qiita.com/yujiroarai/items/a8ee951d1f0e70abaefc
▼sed, awk, grepの使い分け
https://qiita.com/lca367/items/e174ce6985a1e7f3c441
▼【 sed 】 文字列の置換，行の削除を行う
http://tech.nikkeibp.co.jp/it/article/COLUMN/20060227/230879/
▼Linuxで使う正規表現についてまとめました
https://eng-entrance.com/linux-regular-expression#i-12
▼[Linux][sed] 文字列から部分抽出する/置換後に特定文字列を使いまわす方法
https://qiita.com/koara-local/items/2911bd81df2420a420ad

# 3. awkコマンド

## 3-1. 文字列を抽出

```
# 例1: コロン区切りのsample1.logファイル内で左から3番目の文字列を抽出
$ awk -F':' '{print $3}' sample1.log

# 例2: スペース区切りのsample2.logファイル内で左から3番目の文字列を抽出
$ awk '{print $3}' sample2.log
```

- 区切り文字を`-F`で指定(指定しないとスペース区切りになる)

```
# 例3: 文字列abcが含まれる行を検索後それぞれの行に対して文字列aaaをbbbに変換、
#      さらにスペース区切りで左から2番目と3番目の文字列を抽出する
$ cat sample3.log | grep abc | sed s/aaa/bbb/g | awk '{print $2, $3}'
```

# 4. grepコマンド

## 4-1. 文字列を検索

```
grep 文字列 ファイル名
```

- `grep`：ファイルから、指定した文字列が含まれる行を検索する

## 4-2. grepコマンドのオプション

|オプション|意味|
|:--|:--|
|`-c`|指定したパターンにマッチした行数を出力|
|`-h`|ファイル名を先頭に付ける|
|`-i`|大文字と小文字を区別しない|
|`-l`|ファイル名のみを出力する|
|`-n`|マッチした行の行番号を出力する|
|`-s`|エラーを表示しない|
|`-v`|パターンにマッチした行以外を出力する|
|`-w`|パターンを単語としてマッチ|
|`-E`|正規表現を利用してマッチングを行う場合、`-E`で明示しておくと良い|

## 4-3. 正規表現

`grep`コマンドはほとんどの正規表現を利用することができます。
ただし、`grep`の正規表現の記述方法は、ほかのスクリプト言語の正規表現と異なる部分があるので下記にまとめておきます。

|grep|sed|awk|意味|
|:--|:--|:--|:--|
|`.`|`.`|`.`|任意の1文字|
|`*`|`*`|`*`|直前の1文字 or 1パターンの、0回以上の繰り返し|`
|`^`|`^`|`^`|行の先頭|
|`$`|`$`|`$`|行の末尾|
|`\( \)`|`\( \)`|`( )`|パターンのグループ化|
||`\1 \2 \3`|`\1 \2 \3`|後方参照|
|`[ ]`|`[ ]`|`[ ]`|括弧内の任意の1文字|
|`\{n, m\}`|`\{n, m\}`|`{n, m}`|直前の1文字 or 1パターンの、n回以上m回以下の繰り返し|
|`\{n, \}`|`\{n, \}`|`{n, }`|直前の1文字 or 1パターンの、n回以上の繰り返し|
|`\{n\}`|`\{n\}`|`{n}`|直前の1文字 or 1パターンの、n回の繰り返し|
|`\+`|`\+`|`+`|直前の1文字 or 1パターンの、1回以上の繰り返し|
|`\?`||`?`|直前の 1 文字あるいは 1 パターンの 0 回または 1 回だけ出現|

【参考サイト】
▼grep
https://bi.biopapyrus.jp/os/linux/grep.html

# 5. おまけ
## 5-1. hystコマンド

### ▼インストール
OSXの場合はHomebrewでインストールできます。

```
brew install https://raw.githubusercontent.com/winebarrel/hyst/master/homebrew/hyst.rb
```

その他のOSはアーカイブを展開すれば実行できます。

- https://github.com/winebarrel/hyst/releases

### ▼実行方法
#### [数値以外]
数値以外の場合は、それぞれの値をカウントして多い順に並べます。

```
cat test.txt | hyst
```

#### [数値]
`-w`オプションで階級の幅を指定すると、各値を数値として処理します。

```
cat test.txt | hyst -w 50
```

- `-w 50`：階級の幅を50として正規分布のヒストグラムを描画

【参考サイト】
▼hystでコマンドラインでヒストグラムを書く
https://qiita.com/winebarrel/items/95b2f52711cf9811a6c4

### ▼使用例

```
# user_idごとの出現回数（[数値以外]のhystコマンド実行例）

$ cat test.csv | grep -v "user_id" | sed -e "s/^\"[0-9a-zA-Z_]*\",\"\([0-9a-zA-Z_]*\)\"/\1/g" | hyst | head -n 5
L61g2LrwdRV4Q9Eg  2473  ##################################################
DqZIe64440WnwdZY  2086  ##########################################
as1XOU3s3ncgi64U  2070  #########################################
1WtRtO2G0ulE9n0P  1975  #######################################
gZq9GLs1l2jEN11Y  1905  ######################################
```

- ↑自分用メモ：同一ユーザーが何impしたかを、ユーザーごとに出力

```
# 「user_idごとの出現回数」から、数値のみ抽出

$ cat test.csv | grep -v "user_id" | sed -e "s/^\"[0-9a-zA-Z_]*\",\"\([0-9a-zA-Z_]*\)\"/\1/g" | hyst | sed -e "s/^[0-9a-zA-Z_]* *\([0-9]*\) *#*/\1/g" | head -n 5
2473
2086
2070
1975
1905
```

```
# 「user_idごとの出現回数（数値のみ抽出）」のヒストグラム（[数値]のhystコマンド実行例）

$ cat test.csv | grep -v "user_id" | sed -e "s/^\"[0-9a-zA-Z_]*\",\"\([0-9a-zA-Z_]*\)\"/\1/g" | hyst | sed -e "s/^[0-9a-zA-Z_]* *\([0-9]*\) *#*/\1/g" | hyst -w 50
```

- ↑自分用メモ：同一ユーザーが何impしたかを、imp数ごとにユーザー数を出力

## 5-2. 正規表現で数値の範囲を指定する
- 1桁の数値：`[0-9]` or `\d`
- n桁の数字：`[0-9]{n}`
  - 「0001」など、数値としては自然な表現ではない文字列もマッチする
- n桁以上、m桁以下の数字：`[0-9]{n,m}`
- n桁以上の数字：`[0-9]{n,}`
- n桁以下の数字：`[0-9]{,n}`
- n以上m以下の範囲の数字（１桁の場合）：`[n-m]`
- n以上m以下の範囲の数字（１-２桁の場合）：1桁と2桁で場合分け
  - 1以上99以下の数字：`[1-9]|[1-9][0-9]`
  - 3以上13以下の数字：`[3-9]|1[0-2]`
  - 33以上99以下の数字：`3[3-9]|[4-9][0-9]`

【参考サイト】
▼正規表現：数字の桁数、数字の範囲を指定する表現
http://www-creators.com/archives/4241