Envaderにて、Linuxコマンド　tr sed について学ぶ

30代後半、未経験からIT業界のエンジニア転職へ挑戦している、kobakichiです。

今回もいつもお世話になっている[Envader](https://envader.plus/)を利用して、Linuxコマンドを実践しながら学びました。

その振り返りとまとめです。

`Envader`とは、Linuxが環境構築不要で、オンラインで学ぶことができるオンライン学習サービスです。
気になる方は、リンクを貼っておきますので参考にしていただければと思います。

[コース一覧](https://envader.plus/course)
[今回使用したシナリオ](https://envader.plus/course/1/scenario/1019)

今回取り組んだのは、ファイルの中にある文字列を置き換える時に使用するコマンド`tr` `sed`について実践しながら学びました。

## trコマンド
標準入力から読み込まれた文字列を変換したり、削除したりするコマンド。
文字が連続していた場合には、1つにまとめたりもできるらしいです。

### 使い方の基本
```bash
tr オプション 文字列1 文字列2
# 文字列1を文字列2に置き換える。
```

### trコマンド　オプション
|オプション|説明|
|---|---|
|-d|文字列1でマッチした文字列を削除する|
|-s|連続したパターン文字列を1文字としてまとめる|
|-t|文字列1を文字列2の長さに切り詰めてから処理する|
|-c|文字列に含まれない文字全てを対象にする|

クラスという[]を使うことでも`tr`コマンドを使うことができるようです。

|オプション|説明|
|---|---|
|[:alpha:]|英字に変換|
|[:lower:]|英小文字に変換|
|[:upper:]|英大文字に変換|
|[:digit:]|数字に変換|
|[:alnum:]|英数字|
|[:space:]|スペースを入れる|

### 使い方　例

```bash
# trコマンド 小文字から大文字へ変換
envader@172-19-1-2:~$ cat /etc/shells | tr 'a-z' 'A-Z'
# /ETC/SHELLS: VALID LOGIN SHELLS
/BIN/SH
/BIN/DASH
/BIN/BASH
/BIN/RBASH
/USR/BIN/TMUX

# クラスを使った書き方
# 小文字を大文字に変換
cat /etc/shells | tr [:lower:] [:upper:]

# /ETC/SHELLS: VALID LOGIN SHELLS
/BIN/SH
/BIN/DASH
/BIN/BASH
/BIN/RBASH
/USR/BIN/TMUX
```

### -dでマッチした文字列を削除する
```bash
envader@172-19-1-2:~$ cat /etc/shells
# /etc/shells: valid login shells
/bin/sh
/bin/dash
/bin/bash
/bin/rbash
/usr/bin/tmux

#/を削除
envader@172-19-1-2:~$ cat /etc/shells | tr -d /
# etcshells: valid login shells
binsh
bindash
binbash
binrbash
usrbintmux

#/とbを削除
envader@172-19-1-2:~$ cat /etc/shells | tr -d /b
# etcshells: valid login shells
insh
indash
inash
inrash
usrintmux

# /とbとiを削除
envader@172-19-1-2:~$ cat /etc/shells | tr -d /bi
# etcshells: vald logn shells
nsh
ndash
nash
nrash
usrntmux
```
なかなか面白いですね。

### リダイレクトで新しくファイルを作成する

```bash
# binを削除して、リダイレクトで新しくtr.txtファイルを作成する

envader@172-19-1-2:~$ cat /etc/shells
# /etc/shells: valid login shells
/bin/sh
/bin/dash
/bin/bash
/bin/rbash
/usr/bin/tmux

envader@172-19-2-2:~$ cat /etc/shells | tr -d bin > tr.txt

envader@172-19-2-2:~$ cat tr.txt
# /etc/shells: vald log shells
//sh
//dash
//ash
//rash
/usr//tmux
```

こんな使い方もできるんですね。


## sedコマンド
`sed`(Strem Editor)は、テキストファイルに対して編集を行う。
先ほどの`tr`とやっていることは似ているのですが、こちらは指定したファイルをコマンドやスクリプトとして`sed`に指示しておき、その指示に基づいて編集を行い、結果を表示します。

`tr`コマンドと同様、`sed`コマンドも置換や削除はできますが、ファイルを保存する訳ではないのでコマンドを実行した後はファイルの内容は元のファイルに戻ってしまいます。

保存したければ、`-i`オプションをつけてファイルを上書きするようにすれば編集した内容でファイルが保存されます。

### 使い方の基本
```bash
sed オプション コマンド ファイル名
```

```bash
# 複数のコマンドを指定するときは-eオプションをコマンドごとに使う必要がある
sed -e コマンド1 -e コマンド2 ファイル名
```

```bash
# コマンドが書かれたスクリプトファイルを指定するときは-fを使う
sed -f スクリプト　ファイル名
```

上でも少し紹介しましたが、以下オプションです。

### sed　オプション

| オプション | 説明 |
|:-:|:-:|
| -e  | 複数のコマンドを指定する  |
| -f  | コマンドが書かれたスクリプトファイルを指定する  |
| -i  | 処理した内容でファイルを上書きする  |

### sedコマンドで使用できる主なコマンド

| コマンド | 説明 |
|:-:|:-:|
| d  | マッチした行を削除する  |
|  s | パターンに基づいて置換する。最後にgをつけると、マッチ箇所を全て置換する  |
|  y | 文字を変換する  |

### sコマンドを使って色々やってみる。

####  注意点
sコマンド単体だと、検索パターンにマッチした最初の部分しか置換されない。
マッチした部分全部を置換したい場合には、gスイッチと呼ばれるgを最後に付けることで、全部が置換される。

```bash
envader@172-19-1-2:~$ cat sample.txt
samplepaper
papersample
aspapersample
pasampleper
paperpaper

# paperをgetに置換 最初にマッチした箇所しか置換されていない
envader@172-19-1-2:~$ sed s/paper/get/ sample.txt                                 
sampleget
getsample
asgetsample
pasampleper
getpaper

# gスイッチを使ってパターンにマッチした部分を全て置換する
envader@172-19-1-2:~$ sed s/paper/get/g sample.txt                                
sampleget
getsample
asgetsample
pasampleper
getget

# $を付けることで、それぞれの最後に指定した文字を追加することができる
envader@172-19-1-2:~$ sed s/$/get/ sample.txt                                     
samplepaperget
papersampleget
aspapersampleget
pasampleperget
paperpaperget

# paper$とすることで、マッチした最後の箇所のみを置換することができる
envader@172-19-1-2:~$ sed s/paper$/get/ sample.txt                                
sampleget
papersample
aspapersample
pasampleper
paperget

# ^で先頭に指定した文字を追加できる
envader@172-19-1-2:~$ sed s/^/hero/ sample.txt
herosamplepaper
heropapersample
heroaspapersample
heropasampleper
heropaperpaper

# ^paperで、paperの先頭にマッチした文字を置換できる
envader@172-19-1-2:~$ sed s/^paper/grate/ sample.txt
samplepaper
gratesample
aspapersample
pasampleper
gratepaper

# 1~3行目の先頭に>を付ける指定
envader@172-19-1-2:~$ sed '1,3s/^/>/' sample.txt
>samplepaper
>papersample
>aspapersample
pasampleper
paperpaper
```

### sコマンドを複数使用する例
sコマンドを複数指定する場合には、上で紹介したオプション`-e`を使う。
-eを使わないと、複数のsコマンドはエラーになってしまう。

```bash
# -eオプションをつけないと複数のコマンドはエラーになる
envader@172-19-1-2:~$ sed '1,3s/^/>/' '1,3s/$/</' sample.txt
sed: can't read 1,3s/$/</: No such file or directory
>samplepaper
>papersample
>aspapersample
pasampleper
paperpaper

# -eオプションが一つだけだと、これもエラーになる
envader@172-19-1-2:~$ sed -e '1,3s/^/>/' '1,3s/$/</' sample.txt                   
sed: can't read 1,3s/$/</: No such file or directory
>samplepaper
>papersample
>aspapersample
pasampleper
paperpaper

# -eオプションをそれぞれのコマンドに付けることで、しっかりと機能するようになる
# 1~3行目の先頭と末尾に><を付ける
envader@172-19-1-2:~$ sed -e '1,3s/^/>/' -e '1,3s/$/</' sample.txt                
>samplepaper<
>papersample<
>aspapersample<
pasampleper
paperpaper
```

う〜ん、色々な挙動が見れて面白い。

### dコマンド
指定した行を削除する。
注意したいのは、文字列を削除するのではなく、あくまで**行**を削除するということ。

```bash
envader@172-19-1-2:~$ cat sample.txt
samplepaper
papersample
aspapersample
pasampleper
paperpaper

# 1行目のみ削除
envader@172-19-1-2:~$ sed 1d sample.txt
papersample
aspapersample
pasampleper
paperpaper

# 1~3行目までを削除
envader@172-19-1-2:~$ sed 1,3d sample.txt
pasampleper
paperpaper

# 1~3行目までを削除して、sa2.txtとしてリダイレクトして保存
envader@172-19-1-2:~$ sed '1,3d' sample.txt > sa2.txt
envader@172-19-1-2:~$ cat sa2.txt
pasampleper
paperpaper
```

### yコマンド
検索文字にマッチする文字があった場合、その文字を置換文字の同じ位置の文字に置き換える。

#### 注意点
yコマンドは、検索文字と置換文字を同じ長さにしないとエラーになるので注意。

```bash
envader@172-19-1-2:~$ cat sample.txt
samplepaper
papersample
aspapersample
pasampleper
paperpaper

# sa/12は同じ文字数じゃないとエラーになる。
envader@172-19-1-2:~$ sed y/sa/12/ sample.txt
12mplep2per
p2per12mple
21p2per12mple
p212mpleper
p2perp2per

# saを1のみに置換しようとすると、エラーが発生
envader@172-19-1-2:~$ sed s/sa/1/ sample.txt
ed: -e expression #1, char 7: strings for `y' command are different lengths


# sコマンドを使うときは、saを1に置換できる。この差がsとyコマンドの違い
envader@172-19-1-2:~$ sed s/sa/1/ sample.txt
1mplepaper
paper1mple
aspaper1mple
pa1mpleper
paperpaper
```

一瞬、あれ？sコマンドとyコマンドって同じじゃない？と疑問に思ったのですが、こうやって実際にやってみると違いが分かりますね。

### -fオプション
コマンドが書かれたスクリプトファイルを指定して、実行することができるオプション。
今回は環境の関係上実行できなかったので、書き方だけを書いてみます。

```bash
# sedscriptsファイルがあったとする
cat sedscripts
y/sa/12/

# sedscriptsファイルを指定して実行する
sed -f sedscripts sample.txt
# 実行結果
12mplep2per
p2per12mple
21p2per12mple
p212mpleper
p2perp2per
```
このように、予めコマンドと内容を記述したファイルを用意しておいて、`sed`コマンドを実行することもできます。

### -iオプション
`sed`コマンドを実行しただけだと、処理が終わるとファイルの内容は元の状態に戻ってしまいますが、これを処理した内容に上書きするのが`-i`オプション。

実際にやってみる。

```bash
envader@172-19-1-2:~$ cat sample.txt
samplepaper
papersample
aspapersample
pasampleper
paperpape

# -iオプションでファイルを上書き
envader@172-19-1-2:~$ sed -i y/sa/66/ sample.txt

# 無事ファイルが上書きされている
envader@172-19-1-2:~$ cat sample.txt
66mplep6per
p6per66mple
66p6per66mple
p666mpleper
p6perp6per
```
ちゃんと上書きされていますね。
本当、色々な動きがあって面白い。
ちなみに、`-i`オプションを使ったときは、実行結果が出力されないみたいです。

## まとめ

今回は[Envader](https://envader.plus/courses)を使って、`tr` `sed`コマンドについて実践しながら学びました。

私個人的には、`sed`コマンドの方が使いやすい印象を受けました。
オプションやらコマンドやらややこしいので、使う時には確認しながら実行したいと思います。

最後まで読んでいただき、ありがとうございました。

## 参考にした記事等
[Envaderシナリオ](https://envader.plus/course/1/scenario/1019)

LinuC101 102 教科書