MIT講義1 日本語　コース概要＋シェル

#ライセンスとクレジット
クレジット
https://missing.csail.mit.edu/2020/course-shell/
ライセンス
https://creativecommons.org/licenses/by-nc-sa/4.0/

講義動画
https://youtu.be/Z56Jmr9Z34Q

Todo
途中からインラインコードの記述に変更したため修正が必要

***(はじめに)***
エンジニアとして，情報系の学生として学習するときにはじめに教えられるのはプログラムやアルゴリズムだと思います．
しかし，そのプログラムを動かすための環境構築やバグの修正，他の問題にある程度を時間をかけることになります．

ここで紹介しているMITの講座は，より効率よく作業するためのエッセンスが説明されています．
これらは，基本的に知っていて当たり前や，学習しながら問題に直面した時に知るような”小技”のような扱いですが，とても重要だと思っています．
先に知っておくだけでも大きく成長に差が出るはずです．

私は，研究室のゼミで新しく入ってくるメンバーに説明するためのスライド資料をこの講座を利用して作成しよう考えました．
そのときに作成した翻訳やメモを公開しようと思います．

ライセンスはすべてMITのもの準拠します．
追記の部分はメモなどで記します．翻訳間違いなどがあれば，指摘していただけると助かります．

#モチベーション
コンピュータ科学者である私たちは,コンピュータが反復的なタスクを支援するのに優れていることを知っている。
しかし、プログラムに実行させる計算と同じように、コンピュータの使用にも当てはまることを忘れている。

私たちは、コンピュータ関連の問題に取り組む際に、より生産性を高め、より複雑な問題を解決することができる膨大な種類のツールを手元で利用することができる。
しかし、私たちの多くはこれらのツールのごく一部を利用しているに過ぎません。

このクラスはこの問題を解決するための試みです。
私たちは、あなたが知っているツールを最大限に活用する方法を教え、あなたのツールボックスに追加する新しいツールをお見せし、うまくいけば、あなた自身でもっと多くのツールを探究する（そしておそらくは構築する）ことへのワクワク感を植え付けたいと思っています。

これは、我々はほとんどのコンピュータサイエンスのカリキュラムから欠落している学期であると信じているものです。

> 次の項，「Class Structure」は省略しています．

#Topic 1 シェル(Shell)
##シェルとは何か
最近のコンピュータには、コマンドを与えるための様々なインターフェースがある．
架空のグラフィカル・ユーザー・インターフェース、ボイス・インターフェース、さらにはAR/VRまでもインターフェースとしてある．

コンピュータが提供するツールを最大限に活用するためには、昔ながらの方法で、テキストインターフェイスに移行しなければならない．

あなたが手を入れることができるほぼすべてのプラットフォームには、1 つの形または別の形でシェルがあるため，選択するためにいくつかのシェルを持っている．

講義では、Bourne Again Shell、略して「bash」に焦点を当てる．
これは最も広く使われているシェルの一つで、その構文は他の多くのシェルで見られるものと似ています。シェルプロンプト(コマンドを入力することができる)を開くには、最初にターミナルが必要．

お使いのデバイスにはおそらくターミナルがインストールされているか、かなり簡単にインストールすることができる．

>メモ
>ターミナル「黒い画面のやつです」

##シェルを使ってみる
ターミナルを起動すると，このようなプロンプトが表示される．

```bash

missing:~$ 
```

これはシェルへのメインテキストインターフェースです．
プロンプトの意味は
1. あなたがマシン上にいないこと
2. あなたの現在の作業ディレクトリ"current working directory"上にいること
3. 現在いるディレクトリが~(ホームディレクトリの略)であること
4. ＄はrootユーザ出ないこと

上記の４つを示している．
プロンプトではコマンドを入力することができる．
最も基本的なコマンドはプログラムを実行すること．

```bash

missing:~$ date
Fri 10 Jan 2020 11:49:31 AM EST
missing:~$ 
```

ここでは，dataプログラムを実行して，現在の日付と時刻を表示している．
シェルはその後，別のコマンドを実行するか尋ねてくる．
また，引数を指定してコマンドを実行することも可能．

```bash

missing:~$ echo hello
hello
```

この例では，シェルに引数helloを指定してechoプログラムを実行するように命令した．

echoプログラムは単に引数を出力するプログラムです．
シェルはコマンドを空白で分割して解析，最初の単語で示されたプログラムを実行する．

もし，スペースや他の特殊文字を含む引数を提供したい場合
（例えば，ディレクトリの名前が"My Photos")
'や"で引数を囲んで引用する．または，関連する特殊文字だけをエスケープ文字'\'を使って，'My\ Photos'のようにスペースをエスケープすることで引用可能

しかし，シェルはどうやってdateやechoのプログラムを見つけるのか知っているのでしょうか？

シェルはPythonやRubyのようなプログラミング環境なので，変数や条件式，ループ，関数などがあります．（次の講義で紹介

シェルでコマンドを実行するとき，実際にはシェルが解釈するちょっとしたコードを書いていることになります．

もし，シェルがプログラミングのキーワードにマッチしないコマンドを実行するように要求された場合，シェルは'$PATH'と呼ばれる環境変数を参照して，コマンドが与えられたときにシェルがプログラムを変作するディレクトリをリストアップする．

```shell

missing:~$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
missing:~$ which echo
/bin/echo
missing:~$ /bin/echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
```

echoコマンドを実行すると
シェルはechoプログラムを実行すべきであると判断，'$PATH'にある':'で区切られたディレクトリのリストから，その名前のファイル(プログラム)を探します．

ファイルを見つけると，実行可能であれば実行する．
指定したプログラム名に対してどのファイルが実行されているかは，'which program'を使用して調べることができる．

また，実行したいファイルのパスをすることで$PATHを完全に回避することができる．

```shell
hogehoge@local:~$ which echo
/usr/bin/echo
```
>echoプログラムのフォルダは'/usr/bin/echo'にあることがわかる．

##シェルでのナビゲーション
シェル上のパスは，ディレクトリ区切りのリスト．
LinuxとMacOSでは'/'で区切られ，windowsでは'\'で区切られている．

LinuxとMacでは，'/'はファイルシステムの"ルート(階層構造の根)"，でありすべてのディレクトリとファイルはその下にある．
Windowsではディスクパーティションごとにルートがある(例, C:\)

このクラスでは，一般的にLinuxファイルシステムを使用していると仮定している．

'/'で始まるパスは絶対パスと呼ばれる．
それ以外のパスは相対パスです．

相対パスは，現在の作業ディレクトリ(current working directories)からの相対パスで，pwdコマンドで確認でき，cdコマンドで変更できる．
パスの中で，'.'はカレントディレクトリを指し，'..'はその親ディレクトリを指します．

```shell
missing:~$ pwd
/home/missing
missing:~$ cd /home
missing:/home$ pwd
/home
missing:/home$ cd ..
missing:/$ pwd
/
missing:/$ cd ./home
missing:/home$ pwd
/home
missing:/home$ cd missing
missing:~$ pwd
/home/missing
missing:~$ ../../bin/echo hello
hello
```

>メモ：
上記の例だとpwdコマンドはルートディレクトリで実行しています．はじめはユーザのhomeディレクトリ上にアクセスすることが多いです．

シェルプロンプトは，現在の作業ディレクトリが何であるかを知らせてくれることに注意してください．


プロンプトを設定することで，有用な情報をいくつか表示させることができます．これは後ほど説明します．

一般的に，プログラムを実行すると　，特に支持がない限り，カレントディレクトリで動作する．

例えば，プログラムは通常そこでファイルを検索して，必要に応じて新しいファイルを作成する．

指定されたディレクトリに何があるかを確認するには，lsコマンドを使用する．

```shell

missing:~$ ls
missing:~$ cd ..
missing:/home$ ls
missing
missing:/home$ cd ..
missing:/$ ls
bin
boot
dev
etc
home
...
```

ディレクトリが第一引数として与えられない限り，　lsコマンドは現在のディレクトリの内容を表示する．

ほとんどのコマンドは，動作を変更するために'-'で始まるフラグオプション(値を持つフラグ)を受け入れる．

通常，'-h'または'--help'フラグを指定してプログラムを実行すると，どのようなフラグやオプションが利用可能かを示すヘルプテキストが表示される．

例えば，'ls --help'を実行すると，以下のように表示される．

```shell
-l                         use a long listing format
```

```shell
missing:~$ ls -l /home
drwxr-xr-x 1 missing  users  4096 Jun 15  2019 missing
```

これにより，存在する各ファイルはディレクトリに関する多くの情報が得られる．
最初の行頭'd'はディレクトリがないことを示している．
そして，３つの文字の３つのグループ(rwx) は，ファイルの所有者(missing)，所有グループ(users)，および他のすべての人が，それぞれ関連する項目についてどのようなパーミッションを持っているか示している．

>メモ：
パーミッションは権限です．別の講で説明すると思いますが
ユーザやグループごとにファイルやディレクトリの読み込み，書き込み，実行を制御することができます．
権限を設定するコマンドは'chmod'です．
下記で詳細説明します．

'-'は，与えられたプリンシパル(ユーザの総称？)が与えられたパーミッションを持っていないことを示す．

```shell
missing:~$ ls -l /home
drwxr-xr-x 1 missing  users  4096 Jun 15  2019 missing
```

上記では，所有者のみが，ディレクトリを修正(w)すること(その中のファイルを追加/削除すること)が許可されている．

ディレクトリに入るためには，ユーザはそのディレクトリ(およびその親)に対して，"検索"(execute:xで表される)権限を持っていなければならない．

ディレクトリの内容を一覧表示するには，ユーザはそのディレクトリのread(r)パーミッションを持っていなければならない．

ファイルの場合，パーミッションは期待通りです．
>(検索できるという意味合いのハズ）

```shell
missing:~$ ls -l /home
drwxr-xr-x 1 missing  users  4096 Jun 15  2019 missing
```

最後のグループである"everyone else"のために"x"のパーミッションが設定されていることに注意してください．

そうすれば，誰でもこれらのプログラムを実行することができます．


この時点で知っておくと便利な他のプログラムとしては

- mv (ファイルの名前を変更したり移動したりする)
- cp (ファイルをコピーする）
- mkdir (新しいディレクトリを作成する)

プログラムの引数，入力，出力，あるいは一般的にどのように動作するかについて，より多くの情報が必要な場合は，manコマンドを試してみてください．

manコマンドは引数として，コマンドの名前を受け取り，そのマニュアルページを表示する．
qを押して終了します．

```shell

missing:~$ man ls

LS(1)                     BSD General Commands Manual                    LS(1)

NAME
     ls -- list directory contents

SYNOPSIS
     ls [-ABCFGHLOPRSTUW@abcdefghiklmnopqrstuwx1%] [file ...]

DESCRIPTION
     For each operand that names a file of a type other than directory, ls displays its name as well as any requested, associated information.  For each operand that names a file of
     type directory, ls displays the names of files contained within that directory, as well as any requested, associated information.

     If no operands are given, the contents of the current directory are displayed.  If more than one operand is given, non-directory operands are displayed first; directory and non-
     directory operands are sorted separately and in lexicographical order.

     The following options are available:

     -@      Display extended attribute keys and sizes in long (-l) output.

     -1      (The numeric digit ``one''.)  Force output to be one entry per line.  This is the default when output is not to a terminal.

     -A      List all entries except for . and ...  Always set for the super-user.

     -a      Include directory entries whose names begin with a dot (.).

     -B      Force printing of non-printable characters (as defined by ctype(3) and current locale settings) in file names as \xxx, where xxx is the numeric value of the character in
             octal.

ー　省略　ー
```

##プログラムの接続
シェルでは，プログラムに入力ストリームと出力ストリームという２つの主要な「ストリーム」が関連付けられる．

プログラムは入力を読み込もうとするときは，入力ストリームから読み込まれ，何かを表示するときは出力ストリームから表示する．

通常，プログラムの入力と出力は両方ともあなたの端末です．
**つまり，キーボードが入力，画面が出力です．**
***しかし，これらのストリームをリダイレクトすることが可能です．***

最も簡単なリダイレクトの形式は`< file`と`file >`です．
これらを使うと，プログラムの入力と出力のストリームをそれぞれファイルにリダイレクトすることができる．

```shell

missing:~$ echo hello > hello.txt
missing:~$ cat hello.txt
hello
missing:~$ cat < hello.txt
hello
missing:~$ cat < hello.txt > hello2.txt
missing:~$ cat hello2.txt
hello
```

また，`>>`を使ってファイルを追加することもできる．
この種の入出力リダイレクションが最も優れているのは，パイプを使うことです．
`|`(パイプ)を使うと，あるプログラムの出力が別のプログラムの入力になるようにプログラムを「連鎖」させることができる．

```shell
missing:~$ ls -l / | tail -n1
drwxr-xr-x 1 root  root  4096 Jun 20  2019 var
missing:~$ curl --head --silent google.com | grep --ignore-case content-length | cut --delimiter=' ' -f2
219
```

パイプの活用方法については，別の講義で詳しく説明します．

##汎用性の高い強力なツール
ほとんどのUnixライクなシステムでは，"root"ユーザと呼ばれる，1人のユーザが特別な存在です．

rootユーザは（ほとんどの場合)すべてのアクセス制限の上にいて，システム内のあらゆるファイルの作成，読み込み，更新，削除ができます．

誤って何かを壊してしまうのは簡単なので，通常はrootユーザとしてシステムにログインすることはありません．

その代わり，`sudo`コマンドを使用します．
その名の通り，何かを`su`のように`do(行う)`ことができます．
（略して”スーパーユーザ”または"root"です)

あなたが権限拒否(permission denied error)のエラーを取得するとき，あなたがrootとしてなにかを行う必要があるためです．

しかし，あなたが本当にそのようにしたいと思ったことをダブルチェックすることを意識してください．

rootである必要があることの１つは，`/sys`の下にマウントされている`sysfs`ファイルシステムに書き込むこと．
`sysfs`は多くのカーネルパラメータをファイルとして公開しているので，特別なツールを使わなくてもカーネルをその場でかんたんに再設定できます．

WindowsやmacOSにはsysfsが存在しないことに注意してください．

例えば，ラップトップの画面の明るさは

```shell
/sys/class/backlight
```

そのファイルに値を書き込むことで，画面の明るさを変更することができる．

```shell
$ sudo find -L /sys/class/backlight -maxdepth 2 -name '*brightness*'
/sys/class/backlight/thinkpad_screen/brightness
$ cd /sys/class/backlight/thinkpad_screen
$ sudo echo 3 > brightness
An error occurred while redirecting file 'brightness'
open: Permission denied
```

このエラーは意外と出てくるかもしれません．
結局，コマンドを実行したのは`sudo`

これはシェルについて知っておくべき重要なことです．
`|,>,<`のような操作は，個々のプログラムではなく，シェルによって行われる．

`echo`やその仲間たちは`|`について「知っている」わけではありません．

彼らは，入力から読み込んで出力に書き込んでいるだけです．

上の例では，シェル(ユーザとして認証されている)は，`sudo echo`の出力として設定する前に，書き込み用の明るさファイルを開ことしていますが，シェルはrootで実行していないため，それができません．

この知識を利用して，これを回避することができます．

```shell
$ echo 3 | sudo tee brightness
```

`tee`プログラムは`/sys`ファイルを開いて書き込みを行うものであり，`root`として実行されるので，パーミッションはすべてうまく行きます．
様々なシステムLEDの状態など，`/sys`を通して様々な楽しいことや便利なことを制御することができます．
（あなたのパスは異なるかもしれない）

```shell
$ echo 1 | sudo tee /sys/class/leds/input6::scrolllock/brightness
```

##次のステップ
この時点では，基本的なタスクを達成するのに十分なシェルの使い方を知っています．
これで，興味のあるファイルを見つけたり，ほとんどのプログラムの基本的な機能を使うことができるようになるはずです．
次の講義では，シェルを使ってより複雑なタスクを実行したり自動化したりする方法や，便利なコマンドラインプログラムについてお話します．

##演習

1. このコースでは，BashやZSHなどのUnixシェルを使用する必要があります．LinuxやmacOSを使用している場合は，特別なことをする必要はありません．Windowsの場合，cmd.exeやpowerShellを実行していないことを確認する必要があります．Windows Subsystem for Linux(WSL)やLinux仮想マシンを使用して，Unixスタイルのコマンドラインツールを使用することができます．
適切なシェルを実行していることを確認するには，`echo $SHELL`コマンドを実行してください．もし`/bin/bash`や`/usr/bin/zsh`のようなものが表示されたら，それは正しいプログラムを実行していることを意味します．

2. `/tmp`の下に`missing`という新しいディレクトリを作成します．
3. `touch`プログラムを探します．`man`プログラムはあなたの味方です．
4. `touch`を使って`semester in missing`という新しいファイルを作成します．
5. そのファイルに以下の内容を１行ずつ書き込んでください．
    1. 最初の行を動作させるのが難しいかもしれません．
    2. `Bash`では`#`はコメントから始まること，そして`!`はダブルクォート(`"`)文字列の中でも特別な意味を持っていることを知っておくと便利です．
    3. `Bash`はシングルクォート文字列(`'`)を別の意味で扱います．詳しくは`Bash`のクォートマニュアルページを参照してください．https://www.gnu.org/software/bash/manual/html_node/Quoting.html
    
    ```shell
#!/bin/sh
curl --head --silent https://missing.csail.mit.edu
```

6.  ファイルを実行してみてください．つまり，スクリプトのパス(`./semester`)をシェルに入力してEnterキーを押してください．lsの出力をみて，なぜうまくいかないのか理解してください．（ヒント：ファイルのパーミッションビットを確認）
7.  `sh`インタプリタを明示的に起動してコマンドを実行し，第一引数にファイル `semester` を与えます.`./semester`では動かなかったのに、なぜこれが動くのでしょうか？
8.  `chmod`プログラムを検索してください(例: man chmodを使う).
9.  `chmod`を使って, `sh semester`と入力する代わりに `./semester`コマンドを実行できるようにしてください.ファイルが`sh`を使って解釈されることになっていることをシェルはどうやって知っているのでしょうか？詳しくは`shebang`行のこのページを参照してください。https://en.wikipedia.org/wiki/Shebang_(Unix)
10.  `|`と `>` を使用して,学期ごとに出力された「最終更新日」をホームディレクトリの last-modified.txt というファイルに書き出します.
11.  ラップトップのバッテリーの電力レベルやデスクトップのCPU温度を`/sys`から読み出すコマンドを書いてください.注意: もしあなたが macOS ユーザーなら、OS には `sysfs` がないので、この練習はスキップしても構いません.