sudo rm -rf /*した残骸で選択ソートを実装した

## はじめに

いろいろ遊べて楽しいというラズパイを手に入れました。
<img width="1161" alt="スクリーンショット 2020-03-07 12.34.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/1033c528-24c4-664b-bf82-6ad19fb99579.png">
写真は[公式ホームページ](https://www.raspberrypi.org/products/raspberry-pi-4-model-b/)から

とりあえずCUIのみのraspbianを入れて、有名な**破滅の呪文**`sudo rm -rf /*`をやったらどうなるんだろうってことでやってみました。
<img width="699" alt="スクリーンショット 2020-03-07 3.04.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/654e8eb5-f0cd-fcda-0620-1c54eb5f4258.png">
大量に出るエラー

案の定`ls`とか`python`とかいろんなコマンドが使えなくなっていたのですが、中には`cd`のように使えるコマンドもあります。
<img width="571" alt="スクリーンショット 2020-03-07 12.37.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/bc5b17d0-468b-daa7-d097-558165d0f45e.png">
`cd`は使える！

という訳で、`sudo rm -rf /*`に**耐え抜いたコマンド**だけを使ってbashの**いろんな機能を解説しながら選択ソートを実装したい**と思います。消え去ってしまったコマンド(`ls`、`cat`など)は自作しつつ、**入力した数値列を選択ソートしてファイル出力する**ところまでがゴールです。

解説する機能の目次
1. 組み込みコマンド
2. 補完
3. 入出力のリダイレクト
4. シェル変数
5. 関数
6. フロー制御（if、for）
7. 配列

追記(2020年3月24日)
この記事であまり扱えていない`ps`コマンドを実装した記事も書きました。リンクは[こちら](https://qiita.com/minnsou/items/eb90ec4d56920e16e7c7)。

・参考文献
オライリー・ジャパン [入門bash](https://www.oreilly.co.jp/books/4873112540/)

・実行環境
Raspberry Pi 4 Model B+
Raspbian Buster Lite [2020-02-13-raspbian-buster-lite.img](https://www.raspberrypi.org/downloads/raspbian/)

## そもそもbashとは

bashは**UNIXシェル**の1つです。UNIXシェルというのは、簡単に言うと「ユーザーからの入力を受け付け、それをOS（＝UNIX）が理解できる命令に変換して、OSからの出力をユーザーに伝えるプログラム」となります。

UNIXシェルには様々なものがあります。最初に主流になったのは**Bourneシェル**で1979年に初めて搭載されました。`sh`というコマンドで馴染みのある方もいると思います。次に広く使用されたのは**Cシェル**（`csh`）でこれも1970年代後半に開発されています。これらの有名なシェルを元に`tcsh`や`ksh`、`zsh`など様々なシェルが作られました。

`bash`はこれらのUNIXシェルの中でも、標準的なシェルとして位置付けられています。MacOSもはつい最近までデフォルトで`bash`を使用していましたし、Ubuntuなどでは今でも標準のシェルです。

それではいきましょう。

## 1.組み込みコマンド

`cd`や`ls`というコマンドには、**組み込みコマンド**（＝内部コマンド）と**外部コマンド**の2種類があります。**組み込みコマンド**とはシェルに組み込まれているコマンドのことで、**外部コマンド**はシェルとは独立して存在するコマンドのことです。

実際の例を紹介しましょう。例えば`echo`コマンドは、標準出力(ここでは画面)に文字を出力するコマンドです。`echo aaa`というコマンドを実行することで、`aaa`という文字列を出力します。
<img width="268" alt="スクリーンショット 2020-03-11 1.38.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/1315f09c-723c-d828-899b-9c92a474a354.png">
この`echo`コマンドは`bash`に組み込まれていて、ただ`echo`と入力することで、組み込みコマンドである`echo`が実行されます。しかし、`echo`には外部コマンドである`/bin/echo`という実行ファイルもあります。`/bin/echo aaa`と実行すれば、外部コマンドの`echo`が実行されて`aaa`と出力されるはずです。
<img width="356" alt="スクリーンショット 2020-03-11 1.38.58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/941e928d-3d2b-cb6b-23f6-472c6330d4fd.png">
外部コマンドの`/bin/echo`は消えていて、実行に失敗してしまいました。

このように`sudo rm -rf /*`すると**外部コマンドはあらかた消滅**しています。よってこれからは**bashの組み込みコマンドのみ**を使用していきます。ちなみに組み込みコマンドは`help`で全て表示することができます。

## 2.補完

さて、`sudo rm -rf /*`したものの、`ls`コマンドが使えないのでなんのファイルが残っているのかわかりません。ここで、bashの**補完機能**を使います。

補完機能はみなさん使ったことがあると思いますが、最初の何文字かを入力した後にtabキーを押すと、その文字列を補完してくれるやつです。

例えば、`ech`まで入力した後にtabキーを押すと、`echo`と入力されます。
<img width="265" alt="スクリーンショット 2020-03-07 13.36.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/36715a13-4e87-a4b7-3805-b87234f2d271.png">
↓ tab
<img width="265" alt="スクリーンショット 2020-03-07 13.36.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/444ba0d8-d751-7c1e-fbf0-3828c7247fcb.png">
これはコマンド名を補完しているので**コマンド名補完**といいます。

`/`に何のファイルが残っているかは**ファイル名補完**を用います。`/`と入力した後に`Ctrl+x /`と入力するとファイル名としての補完候補を出力してくれます。（なお、tabを２回押すでもOK）
<img width="284" alt="スクリーンショット 2020-03-07 13.39.58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/8228a837-2a3b-31e9-907c-9c97a6c4c733.png">
6個のディレクトリが残っていることがわかりました。それぞれの中身を見ていきましょう。
<img width="1313" alt="スクリーンショット 2020-03-10 23.47.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/641feadb-9697-8c60-c6ff-bfa2228c921c.png">
`/boot`には何も入っていないようなので、`.`の補完だけ表示させておきました。それぞれのファイルの内容を解説し始めると記事が何本もかけてしまうので省略します。基本的にここに残っているのは特殊ファイルだったり、パーミッションなどの関係でrootユーザーでも消せないようなものばかりです。

なお、補完にはファイル名補完、コマンド名補完以外にも、**ユーザー名補完**、**変数名補完**、**ホスト名補完**があります。基本的に補完はtabでなんとかなりますが、補完がうまく行かない時にはこれらの補完を直接指定すると**QOLが上がります**。

## 3.入出力のリダイレクト

次に**リダイレクト**を使ってファイルの生成をします。そもそもUNIXプログラムには以下の共通した3つの機能を備えています。
- **標準入力**という入力の受け付け
- **標準出力**という出力の生成
- **標準エラー出力**というエラーメッセージの生成

基本的に、標準入力はキーボードからの入力で、標準出力と標準エラー出力は画面です。

この標準入出力先は変えることができます。例えば「標準入力を**ファイルから読み込んで**、標準出力も**ファイルに出力**する」などです。標準出力先を変更するには`>`という記号を使います。例えば`echo aaa > bbb.txt`とすると、`aaa`と画面（＝標準出力）に表示されずに、内容が`aaa`である`bbb.txt`という名前のファイルが生成されます。このように入出力先を**リダイレクト**して変更できるのです。

普通`/`ディレクトリ等で上記のファイル生成コマンドを実行すると権限がないと怒られて、ファイルが生成されません。
<img width="333" alt="スクリーンショット 2020-03-11 1.42.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/0d410acc-7d06-acf3-abd9-0c0393434bdc.png">
`sudo`したら作れるのではと思う方もいると思いますが、試しに正常な状態で`/`ディレクトリで`sudo`してもだめでした。まあそもそも今は`sudo`コマンドすら**消えてしまっている**ので、どちらにしろダメですが…

というわけで、今回はルート権限がなくてもファイルが作れるディレクトリを探しました（あたり前ですが、ディレクトリを作成する`mkdir`も権限を変更する`chmod`も今は存在しません）。見つけたのは`/run/user/1000`ディレクトリです。
<img width="446" alt="スクリーンショット 2020-03-07 14.51.41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/3b04d38b-5062-a55f-7ae5-d550c45aec47.png">
エラーが出ませんでした。ファイル名補完でちゃんと表示されるか確認しましょう。
<img width="446" alt="スクリーンショット 2020-03-07 14.53.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/d6353061-84f5-3765-a02e-c4ce76dc9dc8.png">
↓　`ESC+/`でファイル名補完
<img width="446" alt="スクリーンショット 2020-03-07 14.53.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/6194da70-5f50-789e-a8ce-8c59d952a452.png">
無事ファイルが作成されているようです。このファイルの中身がちゃんと`hello`になっているか確認したいですが、`cat`は消えてしまいました。よって**シェル変数**を使って中身を見ていこうと思います。

## 4.シェル変数

ここからはシェルプログラミングの内容に近づいていきます。bashも普通のプログラミング言語と同じように変数という概念を持っています。

変数には`=`で代入します。等号の両端にスペースは入れません。表示するには`$`を使います。
<img width="365" alt="スクリーンショット 2020-03-07 15.14.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/f1b3e662-f307-4526-da68-a8ab0fc7dfc1.png">
この例では`wonderland`という変数に`alice`という文字列を代入しています。

それでは先ほど作成した`hello.txt`の中身を読んでみましょう。これには`read`コマンドを使います。これはシェル変数に値を読み込ませるコマンドです。
![i2NK19pqSM5bhdkPgzJZ1583938839-1583938984.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/1137815f-fe2f-6e76-8a0c-d77bb12e1995.gif)
上の例では、キーボードから入力された`aaa`という文字列を受け取って、シェル変数`val1`に代入しています。この`read`コマンドにおいて、標準入力からの入力をファイルからの入力に変えましょう。それには`<`を使ってリダイレクトします。
<img width="441" alt="スクリーンショット 2020-03-08 2.54.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/343b650a-4150-39ed-2073-ea81d3ed0334.png">
変数`val2`には`hello.txt`に書き込んだ`hello`という文字列がちゃんと入っていることが確認できました！

このシェル変数には、bashを起動した時点で既に組み込まれているものがたくさんあります。

- コマンドの履歴を保存する数を表す`HISTSIZE`
- プロンプト文（この例では`pi@raspberry:/ $`と書かれているところ）を表示するための`PS1`
- ユーザーが入力したコマンドを検索するために使われる`PATH`
- ホームディレクトリを示す`HOME`

などです。`sudo rm -rf /*`されていてもこいつらは参照できます。
<img width="963" alt="スクリーンショット 2020-03-07 15.44.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/f830ebc7-af57-6e24-f445-ce00b285d83f.png">
これらの組み込み変数は全て大文字で記述されています。全ての組み込み変数を見たい方は`declare`を実行してください。

この組み込み変数の中には環境変数と呼ばれる特殊な変数もあります。これは全てのサブプロセスが知ることのできる変数です。上の4つの例でいうと`PATH`と`HOME`は環境変数です。例えばbashからpythonファイルを実行する時に、そのプロセスはコマンドの履歴に関する`HISTSIZE`なんて使いませんが、`PATH`や`HOME`がないとファイルの実行時や保存時に困ることになると思います。

環境変数をすべて見たい方は`export`を実行してください。

## 5.関数

bashでは関数を定義することができます。定義の仕方の一例はこんな感じです。

```
function < 関数名 >
{
   < シェルコマンド >
}
```

実際に関数を作ってみましょう。
<img width="294" alt="スクリーンショット 2020-03-10 23.52.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/d8e66738-4da0-83e2-bef2-7d6f9d66f914.png">
`alice`という名前の関数です。ここでは**位置パラメータ**と呼ばれる組み込み変数を使用しています。位置パラメータは、あるスクリプトが呼び出された時に、そのコマンドライン引数を保持します。`0`にはコマンドの名前、`1`には1つ目の引数、`2`には2つ目の引数、といった感じです。`@`は位置パラメータ`0`を除く全ての引数が、`#`は引数の数を保持します。

これを`alice in wonderland`として実行すると以下のようになります。
<img width="324" alt="スクリーンショット 2020-03-08 12.01.25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/d28f620d-8af6-6740-8985-ac1c5e94aad2.png">
今回の例だと`$3`と`$4`には何も入っていません。

これを使って単純な`cat`コマンドを生成しましょう。引数の1つ目(`$1`)だけをみて、そのファイルの中身を出力する関数です。
<img width="279" alt="スクリーンショット 2020-03-10 23.53.58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/47df0efb-4770-b77e-48d7-25c7d94aba90.png">
実際に使ってみます。`3.入出力のリダイレクト`で作った`hello.txt`の中身を表示させてみましょう。
<img width="397" alt="スクリーンショット 2020-03-08 3.54.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/721c99f0-b84c-644f-cbfe-9f15d57e9ff8.png">
ちゃんと動きました！ [^1]


## 6.フロー制御

bashにもif/else、for、while、until、case、selectなど様々な制御構文がありますが、今回は**if文**と**for文**に話を絞ります。

### 6.1 if文

if文の構文は以下の通り。

```
if < 条件 >
then
    < 一連の文 >
elif < 条件 >
    then < 一連の文 >
else
    < 一連の文 >
fi
```

普通のプログラミング言語と同じようにelifは複数挿入可能で、elifとelseは省略可能です。

ここで大事なのは、ifの`< 条件 >`には通常のブール式ではなく**普通のコマンドが入る**ということです。ではコマンドの何を見て条件式を考えるかというと、**終了ステータス**です。

全てのUNIXコマンドは、終了時に呼び出し元のプロセス（この場合はシェル）に整数コードを返します。これを**終了ステータス**と言い、通常は0が**正常終了**、それ以外が**異常終了**を表します。終了ステータスはシェル変数`?`に自動で代入されます。
<img width="254" alt="スクリーンショット 2020-03-08 1.16.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/e96f1570-b783-7862-8753-ed02c70a4a5c.png">
上の例では`ls`コマンドの実行に失敗しているので終了ステータスに127が入り、`cd`コマンドには成功しているので0が入っています。

ここで、シェルの`test`コマンドを使うことで**数式も評価できる**ようになります。例えば`test 3 -ne 4`なら真なので0を返し（`-ne`は両辺が等しくない(not equal)の意味）、`test 4 -lt 2`なら偽なので1を返します（`-lt`は右辺が左辺より小さい(less than)の意味）。このコマンドを使えばifの中でも適切に真偽判定ができるようになります。
<img width="258" alt="スクリーンショット 2020-03-08 1.20.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/7ce89443-4748-2fb9-d91f-dfe43b3c056c.png">
なお`test`コマンドは`[ ... ]`と書くこともできます。例えば`test 3 -eq 4`は、`[ 3 -le 4 ]`と同値です(`-le`は右辺が左辺以下(less than equal)の意味)。具体的なif文の例を見てみましょう。
<img width="297" alt="スクリーンショット 2020-03-08 1.25.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/adf87057-5a08-a093-34c7-68f721b3aa2d.png">
変数`a`と変数`b`の大小を比べて、echoで結果を出力する例です。今回は`a`の方が小さいので、`a is small`という文字列が返ってきています。

また算術演算に関しては、`$((`と`))`を使って表すこともできます。
<img width="307" alt="スクリーンショット 2020-03-08 1.26.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/41afcc30-08d5-f883-41ab-97120eb1e4cf.png">
記号の意味は基本的にC言語と同じです。ただ注意としては、`$(( 5 > 3 ))`は**真なので1を返します**が、`[ 5 -gt 3]`では**終了ステータスとして0を返します**。気をつけましょう。

### 6.2 for

次にfor文を見ていきます。for文の構文は以下です。

```
for name in list
do
    < $nameを使用する一連の文 >
done
```

`list`にはファイルの名前を指定します。もし`in list`を省略すると、デフォルトでは`$@`（コマンドラインの引数のリスト）になります。for文の簡単な具体例としては以下です。
<img width="455" alt="スクリーンショット 2020-03-11 0.00.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/89cb2ce5-a06e-d0a2-d5e5-41f32058ecd0.png">
見た通り`img1.jpg`から`img3.jpg`までを表示しています。ただこのfor文はもっと簡単に書けます。
<img width="362" alt="スクリーンショット 2020-03-11 0.02.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/342da683-7a32-9832-876c-5bc8d437a0c6.png">
ここでは**ブレース展開**を用いています。例えば`aa{1..3}.c`はbashによって`aa1.c aa2.c aa3.c`と展開されます。他には`,`を用いることもでき、例えば`b{ed, ar}s`は`beds bars`と展開されます。

それではここで、消え去ってしまった`ls`コマンドを作ってみましょう。
<img width="362" alt="スクリーンショット 2020-03-11 0.06.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/35368291-5322-f055-a703-74c93d3ec502.png">
`*`は**ワイルドカード**の一種で、そのディレクトリ内のファイル名の任意の文字列に一致します。`echo *`とすれば`*`はそのディレクトリ内のファイル名に展開されます。それではこの`ls`を動かしてみます。
<img width="218" alt="スクリーンショット 2020-03-08 1.51.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/34a39cc1-27fe-1bac-7d2b-380f16de62b0.png">
ちゃんと動きました！[^2]

実はbashのfor文には、C言語やJavaに近い**算術演算forループ**と呼ばれる構文もあります。構文は以下です。

```
for (( < 初期条件 > ; < 終了条件 > ; < 更新処理 > ))
do
    < 一連の文 >
done
```
これを使って九九の表を作ってみます。
<img width="547" alt="スクリーンショット 2020-03-11 0.14.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/f123def7-07da-f92a-0de1-d0d49785996c.png">
`i`と`j`の2つのfor文を回しています。`echo`の`-n`オプションは１つの数字で勝手に改行をしないようにし、`-e`オプションは`\t`をタブとして出力するために付けています。内側のfor文の終了後の`echo`で改行します。

## 7.配列

これで最後の章です、実はbashでは、配列を使うことができます。例えば次のように定義できます。
<img width="307" alt="スクリーンショット 2020-03-11 0.15.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/5ed5af56-d62f-52dd-7213-f7958bf2e149.png">
この中身を`echo`で見てみましょう。
<img width="286" alt="スクリーンショット 2020-03-11 0.18.32.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/d8fa262f-6deb-5aaf-2951-89525d71c14b.png">

`echo $a`を見るとわかるように、**引数を省略すると0番目の要素**が出てきます。また、`$a[0]`と`${a[0]}`の**違いに注意**してください。実は今まで使用していた`$variable`という構文は`${variable}`の**省略形**でした。`$a[0]`と指定すると、`$a`だけが`12`に変換されるので`12[0]`が出力されます。

また配列には特殊な記号の`@`や`#`を使用することもできます。`@`は配列の全ての要素を参照するもので、配列のインデックスに入れて使用します。
<img width="298" alt="スクリーンショット 2020-03-11 0.21.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/3c0dabae-ad49-3f33-6546-a977f0151d9b.png">
全ての要素が表示されているのが分かります。

また`#`は配列の長さを参照するもので、配列の先頭に付けます。
<img width="279" alt="スクリーンショット 2020-03-11 0.23.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/631b43a6-6b51-1145-a241-db22194e676b.png">
`a[0]`は`12`なので長さが2、`a[@]`は配列全体なので長さは3になります。

それでは、ここまでの構文を使って**ユーザーが入力した数値列を選択ソートをしてファイル出力する**というタスクに取り組みます。

ここでは配列の大きさは5とします。まずはユーザーに数字を入力させ、配列`arr`に代入する関数`input_array`を作ります。
<img width="330" alt="スクリーンショット 2020-03-11 0.27.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/d9cb8bcf-8ac3-b080-b482-ac38b23f0b40.png">
`echo`でユーザー入力を促し、`read`を使って配列に値を代入しています。実際に実行してみましょう。数字は適当に入れます。
![mmgrEJrogA3S14uVS00z1583856061-1583856155.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/1dada57b-a339-f6f2-b852-7596aee73e13.gif)
配列の中身を見てみます。
<img width="282" alt="スクリーンショット 2020-03-11 1.04.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/01cd41a2-68f1-3323-ca61-ac36f6f853ab.png">
配列`arr`に`5`、`12`、`2`、`15`、`7`がちゃんと入りました。

それでは配列`arr`の中身を選択ソートで入れ替えましょう。これは「範囲内の最小の数字を先頭に持っていく」ことを繰り返すことでソートするアルゴリズムです。
<img width="400" alt="スクリーンショット 2020-03-11 0.38.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/9efcc260-88fc-e7bd-d812-174449160d97.png">
ちゃんと動くか確認しましょう。
<img width="400" alt="スクリーンショット 2020-03-11 0.40.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/5c5cecce-8895-50cf-8557-413e94d7c0f6.png">
先ほど入れた`arr`の中身がちゃんとソートされているのが分かります！

これらをまとめる関数`output_array`を作って完成です。
<img width="345" alt="スクリーンショット 2020-03-11 1.06.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/6dfbe811-ce64-2944-2dc5-5c5f9239368d.png">
ソートした結果は`output.txt`に書き出します。この関数を実行してみましょう。
<img width="387" alt="スクリーンショット 2020-03-11 1.08.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/289a54ec-0a17-782b-4311-ab47547b82f4.png">
ちゃんと無事動きました。

## 終わりに

組み込みコマンド`logout`で、ログアウトします。このディスクはもう起動しないので、またSDカードにOSを焼き直します。
<img width="299" alt="スクリーンショット 2020-03-11 2.46.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/945aff34-6c67-f39a-182f-91d71890dc35.png">
実はSSHでラズパイに繋いでいました。`sudo rm -rf /*`してもSSH接続が切れなかったのはちょっと驚きでしたね。

bashにはまだまだ紹介しきれなかった機能がたくさんあります。`ps`コマンドが使えないこともあり、プロセス関連は触れられませんでした。ぜひ皆さんも、`sudo rm -rf /*`した極限状態のシェルプログラミングを試してみてください。


[^1]: この`cat`は不完全で1行しか読み込めません。while文を使って`function cat  { while read val; do echo $val; done; } < $1`と書き直すと最終行までちゃんと表示してくれます。これの解説は次の[記事](https://qiita.com/minnsou/items/eb90ec4d56920e16e7c7)で書きました。
[^2]: ただしこの`ls`は`.`から始まるファイル名は表示してくれません。`function ls {   for i in {.*,*}; do echo $i; done; }`とすれば隠しファイルも表示できるようになります。