ワンライナーでお手軽実行可能なsed入門


# ワンライナー！ワンライナー！

みんな大好きワンライナー。
たくさん使いこなせるとイケメンですね＾＾

複数行のほうがカッコイイぜという変態の方は大変申し訳ありませんが対象外となります。


普通のコマンドはいくつかの引数（オプション等）を覚えればおしまいですが、sedはそう簡単にはいきません。

sedは、ストリームエディタ（Stream EDitor)の略でテキスト処理を行うプログラムです。文字列を置換したりする際によく使いますね。

sedの書式は、vimなんかでもよく使ったりするので積極的に覚えましょう。

# sed書式

```
# スクリプトを直接指定
$ sed -e 'スクリプト'

# スクリプトが記述されているファイルを指定
$ sed -f 'スクリプトファイル名'

# 表示を抑制（明示的にスクリプト中でpをすれば表示も可能）
$ sed -n 'スクリプト'

# 拡張正規表現を使ったスクリプトを記述
$ sed -r '正規表現を使ったスクリプト'
```

スクリプト部分はざっくり言うと以下の様な構成です
「スクリプト＝範囲指定＋コマンド」



# sed使用例

以下BSD sed, GNU sedで動作確認しています。

## 基本的な文字列処理

### マッチした文字列を置換する

```
$ echo 'before text' | sed 's/before/after/'

after text
```

### マッチした文字列をすべて置換する

```
$ echo 'before before before text' | sed 's/before/after/g'

after after after text
```

### 複数の文字列を一度に置換する

```
# before1→afterA、before2→afterBに置換
$ echo 'before1before2' | sed 's/before1/afterA/;s/before2/afterB/'

afterAafterB
```

### 置換時にマッチしたキーワードを再利用する

```
# githubというマッチしたキーワードを&で再利用
$ echo 'github' | sed 's/github/I like &./'

I like github.
```

### １文字づつ置換する

```
# eとoをEとOに置換する
$ echo -e "book\nball\norange\nmelon\npen\nmouse" | sed 'y/eo/EO/'

bOOk
ball
OrangE
mElOn
pEn
mOusE
```


## 範囲を限定したテキスト処理

### 最初にマッチしたものだけ置換する

```
# 1行目からccccのある行までを探索し、ccccをeeeeに置換
# sの前には1,3とか数値だけを指定してもいい
$ echo -e "aaaa\nbbbb\ncccc\ndddd\ncccc" | sed '1,/cccc/s/cccc/eeee/'

aaaa
bbbb
eeee # 最初にマッチしたので置換されている
dddd
cccc # 最初のマッチじゃないため置換されない
```

### 指定範囲の行を削除する

```
# aaaaからccccまでの範囲を削除
$ echo -e "aaaa\nbbbb\ncccc\ndddd" | sed '/aaaa/,/cccc/d'

dddd


# 1行目から２行目までを削除する
$ echo -e "aaaa\nbbbb\ncccc\ndddd" | sed '1,2d'

cccc
dddd


# 空行または#で始まるコメント行削除
$ echo -e '1:one\n2:two\n\n# 3:three\n4:four' | sed '/^$/,/^#/d'

1:one
2:two
4:four
```



### 指定範囲の行を表示する

```
# aaaaからccccまでの範囲を表示
$ echo -e "aaaa\nbbbb\ncccc\ndddd" | sed -n '/aaaa/,/cccc/p'

aaaa
bbbb
cccc

# 3行目から最後の行までの範囲を表示
$ echo -e "aaaa\nbbbb\ncccc\ndddd" | sed -n '3,$p'

cccc
dddd
```

### 指定行以外を表示する

```
# 2行目以外を表示
$ echo -e "aaaa\nbbbb\ncccc\ndddd" | sed -n '2!p'

bbbb
cccc
```


### ファイル内の指定行のみ出力

```
# file1.txtの１０行目を取得
$ sed -n 10p file1.txt
```

### 指定行にデータを挿入

```
# １行目に######を挿入(iはマッチ行の手前)
# echo -e "aaaa\nbbbb\ncccc\ndddd" | sed '1i\######'

######
aaaa
bbbb
cccc
dddd

# 最終行に######を挿入(aはマッチ行の後)
# echo -e "aaaa\nbbbb\ncccc\ndddd" | sed '$a\######'

aaaa
bbbb
cccc
dddd
######
```


## 正規表現を使った文字列置換

実際にコーディングしたり設定を書き換えたりするには正規表現が必須ですね。

### マッチした文字列の一部を再利用する

()でくくれば、置換後文字列で\1,\2,\3・・・のように部分的に再利用できます。
ただし()はそれぞれ\\(,\\)とエスケープしてやる必要があります。

```
# phpのテンプレコードをruby（erb)コードに変換する
$ echo -e "<?php echo 'hogehoge'; ?>" | sed 's/<?php echo \(.*\); ?>/<%= \1 %>/'

<%= 'hogehoge' %>
```


### 最短マッチさせる


```
# <a href="http://www.yahoo.co.jp/" class="others">Yahoo!</a>からURL抽出する

# （NG例）不用意に.*を使うと意図せぬマッチングになってしまう
$ echo -e '<a href="http://www.yahoo.co.jp/" class="others">Yahoo!</a>' | sed 's/<a href="\(.*\)".*$/\1/'

http://www.yahoo.co.jp/" class="others　

# （OK例）最短マッチ ([^"]*で"以外のすべての文字の繰り返しとして指定)
$ echo -e '<a href="http://www.yahoo.co.jp/" class="others">Yahoo!</a>' | sed 's/<a href="\([^"]*\)".*$/\1/'

http://www.yahoo.co.jp/
```


# 応用例

## ログ監視 

### 指定日・指定時間のaccess_logを見る

※apacheのログ書式に応じてフォーマットを見なおしてください

```
# 02/May/2014を含む行を表示
$ cat /var/log/apache2/access_log | sed -n '/02\/May\/2014/p'

# 02/May/2014の22時台の行を表示
$ cat /var/log/apache2/access_log | sed -n '/02\/May\/2014:22/,/02\/May\/2014:23/p
```

### root以外のsyslogを見る

grep -vでもいいんですけどね・・・。

```
# rootを含まない行を表示
$ cat /var/log/syslog | sed -n '/root/!p'
```


## Web解析

### WebページからURL一覧抽出

わざわざプログラム組まなくてもできます。

```
# hrefの中身だけを抽出してリストアップ
# １行に２つ以上aタグがある場合は対応できていません。
$ curl http://b.hatena.ne.jp/hotentry/it | sed -n 's/^.*href="\([^"]*\)".*$/\1/p'
```


### Webページから記事タイトルを抽出してリストアップ

```
# 上とは別の例としてegrepでaタグに絞込み
# その後sedでタグを除去し、行頭のスペースを除去
$ curl http://b.hatena.ne.jp/hotentry/it | egrep "<a.*entry-link" | sed 's/<[^>]*>//g;s/ //g'
```