sed と awk （４）sed - print and substitute

# 最初のサンプル

## プリントコマンド

`p`コマンドを指定すると、現在の pattern space を表示する。ただし、課題があって、下記のように２行ずつ出てくる。なぜ２行出てくるか？というと、stdout と pattern space の両方がデフォルトで表示されてしまうから。

```bash
$ sed 'p' /etc/passwd
root:x:0:0:root:/root:/bin/bash
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
　　　：
```

ちなみに、`pattern space` はなんだろな？と思ったのだけど、これは、sed 内のバッファのことで、ファイルなどから読み取ると、バッファに入るけどそのことらしい。

* [sed(1) - Linux man page](https://linux.die.net/man/1/sed)
* [The Concept of 'Hold space' and 'Pattern space' in sed](https://stackoverflow.com/questions/12833714/the-concept-of-hold-space-and-pattern-space-in-sed)

それを止めるためには、上のマニュアルに表記があるけど、`-n`を使うと、自動でのpattern space の表示をやめるので、１行のみになる。

```bash
$ sed -n 'p' /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
```

ちなみに、Range や、Pattern も指定することができる。最初のは何行目を表示するか？

```bash
$ sed -n '1,3 p' /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
$ sed -n '/^root/ p' /etc/passwd
root:x:0:0:root:/root:/bin/bash
```

# print コマンド 他のパターン

_test_ を使ってみる

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

５行目から最後`$` まで

```bash

$ sed -n '5,$ p ' test
  b
 #bb
bbb
#end
ab12 7af
a234 7ef
```

正規表現も使う。`/ /` でくくって使う

```bash
$ sed -n '/^a/ p ' test
ab12 7af
a234 7ef
$ sed -n '/^a[0-9]/ p ' test
a234 7ef
```
# substitute コマンド

s コマンドは置換する。s に続く文字がデリミタになる。大抵は`/` を使う。下記のコマンドは、特定の範囲の`<string>`を`<replacement>`で置き換える。

```bash
$ sed ' [range] s/<string>/<replacement>/ ' /etc/passwd
```

デリミタは`/`以外にしても良いので下記のようなものも良い。下記のものは、tsuyoshiで始まる行の`/bin/bash`を`/bin/sh`に置き換えている

```bash
$ sed ' /^tsuyoshi/ s@/bin/bash@/bin/sh@ ' /etc/passwd
```

## サンプル

こんなシェルファイルがあります。nl は標準出力に行番号を添付して送るコマンドです。

* [number lines of files](https://linux.die.net/man/1/nl)

```bash 
$ nl parsecsv.sh
     1	#!/bin/bash
     2	OLDIFS=$IFS
     3	IFS=","
     4	while read product price quantity
     5	do
     6	echo -e "\e[1;33m$product \
     7	========================\e[0m\n\
     8	Price : \t $price \n\
     9	Quantity : \t $quantity \n"
    10	done < $1
    11	IFS=$OLDIFS
```

### 指定した行にインデントをつける

レンジを指定して、sコマンドで、先頭行をスペースに置換すればOKです。最後の、gは、hold space をpattern space にコピーもしくは追加するためのコマンドです。両方のコンセプトは次の記事が良い感じです。ホールドスペースは、保存用の領域で、`h` コマンドで、pattern space から、hold space に保存することができます。

* [sedのパターンスペース・ホールドスペースの動作を図で学ぶ](https://qiita.com/gin_135/items/773fec1343a69c9f90d6)

```bash
$ sed ' 6,9 s/^/    /g' parsecsv.sh
#!/bin/bash
OLDIFS=$IFS
IFS=","
while read product price quantity
do
    echo -e "\e[1;33m$product \
    ========================\e[0m\n\
    Price : \t $price \n\
    Quantity : \t $quantity \n"
done < $1
IFS=$OLDIFS
```

同じ感じで、自動での、pattern space 出力をやめて、`p` オプションで、現在のpattern space の出力を指定すると、マッチした部分のみの表示になります。

```bash
$ sed -n ' 6,9 s/^/    /p' parsecsv.sh
    echo -e "\e[1;33m$product \
    ========================\e[0m\n\
    Price : \t $price \n\
    Quantity : \t $quantity \n"
```

先ほど習った通り、デリミタが`/`で都合が悪い時は他のものも使えます。パスなどはその典型例です。

```bash
$ sed -n ' /^azure/ s@/bin/bash@/bin/sh@p ' /etc/passwd
azureuser:x:1000:1000:Ubuntu:/home/azureuser:/bin/sh
```

* [sed(1) - Linux man page](https://linux.die.net/man/1/sed)

# まとめ

まだ、先ほどの、`g`を指定した意義がわかっていません。実際無くしても結果は同じだし、hold space に保存していない限り結果はかわらなさそう。もう少ししたらわかるかな。