MIT講義5 日本語　Command-line Environment

#ライセンスとクレジット
クレジット
https://missing.csail.mit.edu/2020/command-line/
ライセンス
https://creativecommons.org/licenses/by-nc-sa/4.0/
講義動画
https://www.youtube.com/watch?v=e8BO_dYxk5c
Todo

#コマンドライン環境
この講義では、シェルを使用する際のワークフローを改善するためのいくつかの方法を紹介します。
これまでシェルを使ってきましたが、主にさまざまなコマンドを実行することに焦点を当ててきました。
ここでは、複数のプロセスを追跡しながら同時に実行する方法、特定のプロセスを停止または一時停止する方法、プロセスをバックグラウンドで実行する方法について説明します。

また、エイリアスを定義したり、ドットファイルを使って設定したりすることで、シェルやその他のツールを改善するさまざまな方法についても学びます。
これらの方法は、長いコマンドを入力しなくても、すべてのマシンで同じ設定を使用することができるなど、時間の節約につながります。

ここでは、SSH を使ってリモートマシンを操作する方法を見ていきます。

#ジョブの制御
例えば、コマンドの実行に時間がかかりすぎている場合（非常に大きなディレクトリ構造を検索する`find`など）、実行中のジョブを中断する必要がある場合があります。
ほとんどの場合、`Ctrl-C`を押すと、コマンドが停止します。
しかし、実際にはどのように動作するのでしょうか。
また、なぜプロセスの停止に失敗することがあるのでしょうか。

##プロセスの停止
あなたのシェルは、シグナルと呼ばれるUNIXの通信メカニズムを使って、プロセスに情報を伝えています。
プロセスはシグナルを受け取ると、実行を停止し、シグナルを処理し、シグナルが伝えた情報に基づいて実行の流れを変える可能性があります。

このような理由から、シグナルはソフトウェアの割り込みである。
今回の例では、`Ctrl-C`を入力すると、シェルがプロセスに`SIGINT`シグナルを配信するように促します。
ここでは、`SIGINT`を捕捉し、それを無視して、もはや停止しないPythonプログラムの最小限の例を示しています。
このプログラムを終了させるには、`Ctrl-\`を入力することで、代わりに`SIGQUIT`シグナルを使用することができます。

```python
#!/usr/bin/env python
import signal, time

def handler(signum, time):
    print("\nI got a SIGINT, but I am not stopping")

signal.signal(signal.SIGINT, handler)
i = 0
while True:
    time.sleep(.1)
    print("\r{}".format(i), end="")
    i += 1
```

このプログラムに `SIGINT` を 2 回送り、その後に `SIGQUIT` を送るとどうなるかを示します。
なお、`^` はターミナルで入力されたときの `Ctrl` の表示方法です。

```bash
$ python sigint.py
24^C
I got a SIGINT, but I am not stopping
26^C
I got a SIGINT, but I am not stopping
30^\[1]    39913 quit       python sigint.py
```

`SIGINT`と`SIGQUIT`は通常、ターミナル関連の要求に関連していますが、プロセスの潔白な終了を要求するより一般的なシグナルは`SIGTERM`シグナルです。
このシグナルを送信するには、`kill`コマンドを使います。

`kill -TERM <PID>`

##プロセスの一時停止とバックグラウンド化
シグナルはプロセスを`kill`する以外にも様々なことができます。
例えば、`SIGSTOP` はプロセスを一時停止します。
ターミナルで `Ctrl-Z` を入力すると、`Terminal Stop（ターミナルストップ）`の略である `SIGTSTP` シグナルを送信するようにシェルが促します（つまりターミナル版の `SIGSTOP`）。

一時停止したジョブをフォアグラウンドで続行するには `fg` を、バックグラウンドで続行するには `bg` をそれぞれ使用します。

`jobs` コマンドは、現在の端末セッションに関連する未完了のジョブを一覧表示します。
これらのジョブは`pid`で参照することができます（`pgrep`で調べることができます）。

より直感的には、パーセント記号の後にジョブ番号（jobsで表示）を付けてプロセスを参照することもできます。

最後にバックグラウンドで実行されたジョブを参照するには、特別なパラメータ`$!`を使用します。
もうひとつ知っておいていただきたいのは、コマンドの中に「`&`」というサフィックスをつけると、そのコマンドがバックグラウンドで実行され、プロンプトが戻ってきます。

既に起動しているプログラムをバックグラウンドにするには、`Ctrl-Z` に続いて `bg` を実行します。
バックグラウンドになったプロセスは、まだターミナルの子プロセスであり、 `ターミナルを閉じると死んでしまうこと`に注意してください (これにより、さらに別のシグナル `SIGHUP` が送られます)。

これを防ぐには、`nohup (SIGHUP を無視するラッパー)` を使ってプログラムを実行するか、プロセスがすでに開始されている場合は `disown` を使います。
また、次のセクションで説明するように、ターミナルマルチプレクサを使用することもできます。

以下に、これらのコンセプトの一部を紹介するサンプルセッションを示します。

```bash
$ sleep 1000
^Z
[1]  + 18653 suspended  sleep 1000

$ nohup sleep 2000 &
[2] 18745
appending output to nohup.out

$ jobs
[1]  + suspended  sleep 1000
[2]  - running    nohup sleep 2000

$ bg %1
[1]  - 18653 continued  sleep 1000

$ jobs
[1]  - running    sleep 1000
[2]  + running    nohup sleep 2000

$ kill -STOP %1
[1]  + 18653 suspended (signal)  sleep 1000

$ jobs
[1]  + suspended (signal)  sleep 1000
[2]  - running    nohup sleep 2000

$ kill -SIGHUP %1
[1]  + 18653 hangup     sleep 1000

$ jobs
[2]  + running    nohup sleep 2000

$ kill -SIGHUP %2

$ jobs
[2]  + running    nohup sleep 2000

$ kill %2
[2]  + 18745 terminated  nohup sleep 2000

$ jobs
```

特別なシグナルとして`SIGKILL`があります。

これはプロセスがキャプチャできないため、常にプロセスを直ちに終了させることができます。
しかし、ゾンビとなった子プロセスを残してしまうなどの悪い副作用があります。

これらのシグナルやその他のシグナルについては、ここで詳しく説明していますし、`man signal`や`kill -t`と入力することもできます。

#ターミナルマルチプレクサ

コマンドラインインターフェイスを使用していると、複数のものを同時に実行したくなることがよくあります。
例えば、エディタとプログラムを同時に実行したい場合などです。
これを実現するには、新しいターミナルウィンドウを開く必要がありますが、ターミナルマルチプレクサを使用する方がより汎用的なソリューションです。

`tmux`のようなターミナルマルチプレクサは、ペインやタブを使ってターミナルウィンドウを多重化することができ、複数のシェルセッションを操作することができます。

さらに、ターミナルマルチプレクサを使えば、現在のターミナルセッションを切り離して、後から再接続することもできます。
これにより、リモートマシンで作業する際に、`nohup` などのトリックを使用する必要がなくなり、ワークフローが大幅に改善されます。

最近、最も人気のあるターミナルマルチプレクサは`tmux`です。
`tmux`は高度な設定が可能で、関連するキーバインドを使用することで、複数のタブやペインを作成し、それらを素早くナビゲートすることができます。

`tmux` はあなたがそのキーバインドを知っていることを期待しており、それらはすべて` <C-b> x` という形をしています。
これは (1) `Ctrl+b` を押し、(2) `Ctrl+b` を離し、(3) `x` を押すことを意味します。

- `Sessions` :セッションとは、1つまたは複数のウィンドウを持つ独立したワークスペースのことです。
    - `tmux` は新しいセッションを開始します。
    - `tmux new -s NAME` は、その名前でセッションを開始します。
    - `tmux ls` は現在のセッションの一覧を表示します。
    - `tmux`の中で `<C-b> d` と入力すると、現在のセッションを切り離します。
    - `tmux a` は最後のセッションをアタッチします。`t` フラグを使用して、どのセッションを切り離すかを指定できます。
- `Windows` - エディタやブラウザのタブに相当し、視覚的には同じセッションの別の部分となります。
    - `<C-b> c` 新しいウィンドウを作成します。これを閉じるには、<C-d> を実行しているシェルを終了させればよい。
    - `<C-b> N` N番目のウィンドウに移動します。番号が付いています
    - `<C-b> p` 前のウィンドウに移動します。
    - `<C-b> n` 次のウィンドウに移動します。
    - `<C-b> ,` 現在のウィンドウの名前を変更する
    - `<C-b> w` 現在のウィンドウを一覧表示
- `Panes` - `vim` の分割と同様に、ペインでは複数のシェルを同じビジュアルディスプレイに表示することができます。
    - `<C-b> "` 現在のペインを水平方向に分割します。
    - `<C-b> %` 現在のペインを垂直方向に分割
    - `<C-b> <direction>` 指定された方向にあるペインに移動します。ここでいう方向とは、矢印キーを意味します。
    - `<C-b> z` 現在のペインのズームを切り替えます。
    - `<C-b> [` スクロールバックを開始します。その後、<space>で選択を開始し、<enter>でその選択範囲をコピーすることができます。
    - `<C-b> <space>` ペインの配置を循環させます。

>memo
tmuxのショートカットは変更できます．個人的に常用するペインの水平垂直分割をそれぞれ`-`, `|`を使うと，使い始めは楽でおすすめです．


さらに、`tmux`についての簡単なチュートリアルと、オリジナルの`screen`コマンドについてのより詳細な説明があります。
また、ほとんどのUNIXシステムにインストールされている`screen`についても理解しておくとよいでしょう。
https://www.man7.org/linux/man-pages/man1/screen.1.html

#エイリアス
多くのフラグや冗長なオプションを含む長いコマンドを入力するのは面倒なことです。
そのため、ほとんどのシェルはエイリアスをサポートしています。
シェルのエイリアスとは、他のコマンドを短く表現したもので、シェルが自動的に置き換えてくれます。
たとえば、bashのエイリアスは次のような構造になっています。

```bash
alias alias_name="command_to_alias arg1 arg2"
```

なお、等号「=」の周りにはスペースがありませんが、これはエイリアスが単一の引数を取るシェルコマンドだからです。

エイリアスには便利な機能がたくさんあります。

```bash
# Make shorthands for common flags
alias ll="ls -lh"

# Save a lot of typing for common commands
alias gs="git status"
alias gc="git commit"
alias v="vim"

# Save you from mistyping
alias sl=ls

# Overwrite existing commands for better defaults
alias mv="mv -i"           # -i prompts before overwrite
alias mkdir="mkdir -p"     # -p make parent dirs as needed
alias df="df -h"           # -h prints human readable format

# Alias can be composed
alias la="ls -A"
alias lla="la -l"

# To ignore an alias run it prepended with \
\ls
# Or disable an alias altogether with unalias
unalias la

# To get an alias definition just call it with alias
alias ll
# Will print ll='ls -lh'
```

デフォルトでは、エイリアスはシェルセッションを持続させないことに注意してください。
エイリアスを持続させるには、`.bashrc`や`.zshrc`などのシェル起動ファイルにエイリアスを含める必要があります。

#ドットファイル

多くのプログラムは、ドットファイルと呼ばれる平文のファイルを使って設定されています（ファイル名が `.`で始まるため、例えば `~/.vimrc` のように、デフォルトではディレクトリリストの ls に隠れるようになっています）。

シェルは、このようなファイルで構成されたプログラムの一例です。
起動時に、シェルは多くのファイルを読み込んで設定を読み込みます。
シェルによっては、ログインや対話を開始するかどうかに関わらず、プロセス全体が非常に複雑になります。

このテーマに関する優れた資料があります。

`bash` の場合、ほとんどのシステムでは `.bashrc` または `.bash_profile` を編集することができます。
ここには、先ほど説明したエイリアスやPATH環境変数の変更など、起動時に実行したいコマンドを含めることができます。

実際、多くのプログラムは、そのプログラムのバイナリが見つかるように、
`export PATH="$PATH:/path/to/program/bin "`のような行をシェル設定ファイルに含めることを求めます。

ドットファイルで設定できるツールの例としては、他にも以下のようなものがあります。

- bash - ~/.bashrc, ~/.bash_profile
- git - ~/.gitconfig
- vim - ~/.vimrc および ~/.vim フォルダ
- ssh - ~/.ssh/config
- tmux - ‾/.tmux.conf


ドットファイルはどのように整理すればよいでしょうか？
独自のフォルダに入れ、バージョン管理を行い、スクリプトを使ってシンボリックリンクで固定するべきです。

これには次のような利点があります。

- 簡単なインストール：新しいマシンにログインした場合、カスタマイズの適用には1分しかかかりません。
- 移植性：あなたのツールはどこでも同じように動作します。
- 同期：どこにいてもdotfilesを更新でき、すべてを同期させることができます。
- 変更の追跡：あなたはおそらく、あなたのプログラミングキャリア全体にわたって `dotfiles` を維持することになるでしょう。


あなたの `dotfiles` には何を入れるべきですか？
あなたのツールの設定については、オンラインドキュメントや `man` ページを読むことで学ぶことができます。
もう一つの素晴らしい方法は、インターネットで特定のプログラムに関するブログ記事を検索することです。
カスタマイズについて学ぶもう一つの方法は、他の人の `dotfiles` を見ることです: Github でたくさんの `dotfiles` リポジトリを見つけることができます
 最も人気のあるものはこちらです (設定をやみくもにコピーしないことをお勧めします)。
このトピックに関する別の良いリソースはこちらです。
https://github.com/search?o=desc&q=dotfiles&s=stars&type=Repositories
https://github.com/mathiasbynens/dotfiles
https://dotfiles.github.io/

このクラスの講師は全員、GitHub で彼らの dotfiles を公開しています。Anish, Jon, Jose.
https://github.com/anishathalye/dotfiles
https://github.com/jonhoo/configs
https://github.com/jjgo/dotfiles

##移植性

`dotfiles` の一般的な問題点は、複数のマシンで作業する際に、例えば異なるオペレーティングシステムやシェルを使用している場合、設定が機能しない可能性があることです。
また、ある設定を特定のマシンにのみ適用したい場合もあります。

これを簡単に行うための方法がいくつかあります。
設定ファイルがそれをサポートしている場合、`if-statement`に相当するものを使って、マシン固有のカスタマイズを適用します。

例えば、シェルには次のようなものがあります。

```bash

if [[ "$(uname)" == "Linux" ]]; then {do_something}; fi

# Check before using shell-specific features
if [[ "$SHELL" == "zsh" ]]; then {do_something}; fi

# You can also make it machine-specific
if [[ "$(hostname)" == "myServer" ]]; then {do_something}; fi
```

設定ファイルがそれをサポートしている場合は、`include` を利用します。
たとえば、`~/.gitconfig` には設定を入れることができます。

```bash
[include]
    path = ~/.gitconfig_local
```

そして各マシンでは、`~/.gitconfig_local` にマシン固有の設定を入れることができます。
マシン固有の設定を別のリポジトリで管理することもできます。

このアイデアは、さまざまなプログラムで設定を共有したい場合にも便利です。
たとえば、`bash` と `zsh` の両方で同じエイリアスを共有したい場合は、`.aliases` の下にエイリアスを記述し、両方で次のようなブロックを作成します。

```bash

# Test if ~/.aliases exists and source it
if [ -f ~/.aliases ]; then
    source ~/.aliases
fi
```

#リモートマシン
プログラマーが日常業務でリモートサーバを使用することは、ますます一般的になってきています。
バックエンド・ソフトウェアをデプロイするためにリモート・サーバーを使用する必要がある場合や、より高い計算能力を持つサーバーが必要な場合、最終的には`Secure Shell (SSH)`を使用することになります。

ここで取り上げるほとんどのツールと同様に、`SSH`は高度な設定が可能なので、学んでおく価値があります。
サーバーにログインするには、次のようなコマンドを実行します。

```bash
ssh foo@bar.mit.edu
```

ここでは、`bar.mit.edu` というサーバに `foo` というユーザで `ssh` しようとしています。
サーバは、`URL (bar.mit.edu など)` または `IP (foobar@192.168.1.42 など)` で指定できます。
後で見てみると、`ssh` の設定ファイルを修正すれば、`ssh bar` のようにしてアクセスできることがわかります。

##コマンドの実行
`ssh foobar@server ls `は、`foobar` のホームフォルダで `ls` を実行します。
パイプを使って動作するので、`ssh foobar@server ls | grep PATTERN` は、ローカルで `ls` のリモート出力を `grep` し、`ls | ssh foobar@server grep PATTERN` は、リモートで `ls` のローカル出力を `grep` します。


##SSHキー
鍵ベースの認証では、公開鍵暗号方式を利用して、クライアントが秘密の秘密鍵を所有していることを、鍵を明かすことなくサーバーに証明します。

この方法では、毎回パスワードを再入力する必要がありません。
秘密鍵 (多くの場合 `~/.ssh/id_rsa`、最近では `~/.ssh/id_ed25519`) は事実上のパスワードのように扱います。

###鍵の生成
鍵のペアを生成するには、`ssh-keygen` を実行します。

```bash
ssh-keygen -o -a 100 -t ed25519 -f ~/.ssh/id_ed25519
```

秘密鍵を手に入れた人が、認証されたサーバにアクセスできないように、パスフレーズを決めておく必要があります。

`ssh-agent` や `gpg-agent` を使えば、いちいちパスフレーズを入力する必要はありません。

`SSH` 鍵を使って `GitHub` にプッシュする設定をしたことがある人は、おそらくここで説明した手順を実行して有効な鍵ペアを手に入れていることでしょう。
https://help.github.com/articles/connecting-to-github-with-ssh/

パスフレーズがあるかどうかを調べて検証するには、`ssh-keygen -y -f /path/to/key` を実行します。

###鍵による認証
`ssh` は `.ssh/authorized_keys` を調べて、どのクライアントを入れるべきかを決定します。

公開鍵をコピーするには、次のようにします。

```bash
cat .ssh/id_ed25519.pub | ssh foobar@remote 'cat >> ~/.ssh/authorized_keys'
```

よりシンプルな解決策は、`ssh-copy-id` が利用できる場合には、それを使って実現できます。

```bash
ssh-copy-id -i .ssh/id_ed25519.pub foobar@remote
```

##SSHでファイルをコピーする
sshでファイルをコピーするには、いろいろな方法があります。

- `ssh+tee`, 最も簡単な方法は、`ssh` コマンドの実行と `STDIN` の入力を使って `cat localfile | ssh remote_server tee serverfile` とすることです。`tee` は `STDIN` からの出力をファイルに書き込むことを思い出してください。
- `scp` 大量のファイルやディレクトリをコピーする際には、パスを簡単に再検索できる `secure copy scp` コマンドが便利です。構文は `scp path/to/local_file remote_host:path/to/remote_file` です。
- `rsync`は`scp`を改良して、ローカルとリモートで同一のファイルを検出し、再コピーを防止します。また、シンボリックリンクやパーミッションをより細かく制御することができ、`--partial` フラグのような特別な機能もあり、以前に中断したコピーから再開することができます。


##ポートフォワーディング

多くの場面で、マシンの特定のポートをリッスンするソフトウェアに遭遇します。
このような場合、ローカルマシンでは`localhost:PORT`または`127.0.0.1:PORT`と入力しますが、ネットワーク/インターネットを通じてポートを直接利用できないリモートサーバーではどうすればよいのでしょうか。

これはポートフォワーディングと呼ばれるもので、2つの種類があります。
`「ローカルポートフォワーディング」`と`「リモートポートフォワーディング」`です（詳しくは写真をご覧ください。写真のクレジットはStackOverflowのこの記事から引用しています）。
https://unix.stackexchange.com/questions/115897/whats-ssh-port-forwarding-and-whats-the-difference-between-ssh-local-and-remot

- local port forwarding
![a28N8.png .png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391570/c68519d4-5616-20d5-3d1e-683e1f0dd17a.png)

- remote port forwarding
![4iK3b.png .png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391570/20d5ed53-8622-4be9-8aa8-e1a3b399db8c.png)

最も一般的なシナリオは、`ローカルポートフォワーディング`で、リモートマシンのサービスがあるポートをリッスンしており、ローカルマシンのポートをリンクしてリモートポートに転送したい場合です。

例えば、`ポート8888`をリッスンするリモートサーバでjupyter notebookを実行したとします。
これを`ローカルポート9999`に転送するには、 
`ssh -L 9999:localhost:8888 foobar@remote_server` とし、ローカルマシンの `locahost:9999` に移動します。

##SSHの設定
これまで、渡すことのできる多くの引数について説明してきました。
魅力的な代替案は、次のようなシェルエイリアスを作ることです。

```bash
alias my_server="ssh -i ~/.id_ed25519 --port 2222 -L 9999:localhost:8888 foobar@remote_server
```

しかし、`~/.ssh/config` を使うより良い方法があります。

```bash
Host vm
    User foobar
    HostName 172.16.174.141
    Port 2222
    IdentityFile ~/.ssh/id_ed25519
    LocalForward 9999 localhost:8888

# Configs can also take wildcards
Host *.mit.edu
    User foobaz
```

エイリアスではなく `~/.ssh/config` ファイルを使うことのさらなる利点は、 `scp, rsync, mosh` などの他のプログラムでもこのファイルを読み込んで、 設定を対応するフラグに変換できることです。

`~/.ssh/config` ファイルはドットファイルとみなすことができ、 一般的には、残りのドットファイルと一緒に入れても問題ないことに注意してください。
しかし、もしこれを公開すると、インターネット上の見知らぬ人に情報を提供することになりかねません。
これは、ある種の攻撃を容易にする可能性がありますので、SSH設定を公開する際には慎重に行ってください。

サーバー側の設定は、通常、`/etc/ssh/sshd_config` で指定します。
ここでは、`パスワード認証の無効化、ssh ポートの変更、X11 フォワーディングの有効化`などの変更を行うことができます。
コンフィグ設定は、ユーザごとに指定することができます。

###その他
リモートサーバに接続するときによくあるのが、コンピュータのシャットダウンやスリープ、ネットワークの変更などによる接続の切断です。
また、接続に大きなラグがある場合、sshの使用は非常にフラストレーションの溜まるものです。
モバイルシェルであるMoshは、ローミング接続や断続的な接続を可能にし、インテリジェントなローカルエコーを提供するなど、sshを改良しています。

`sshfs` は、リモートサーバ上のフォルダをローカルにマウントして、ローカルエディタを使用することができます。

#シェルとフレームワーク
シェルツールとスクリプティングでは、`bash`シェルを取り上げました。
`bash`は圧倒的にどこにでもあるシェルで、ほとんどのシステムでデフォルトのオプションとして用意されているからです。
しかし、bashが唯一の選択肢というわけではありません。

例えば、`zsh`シェルは`bash`のスーパーセットで、以下のような便利な機能を備えています。

- よりスマートなグロビング、**
- インライングロブ/ワイルドカード展開
- スペルの修正
- より良いタブ補完/選択
- パス展開 (`cd /u/lo/b` は `/usr/local/bin` として展開)

フレームワークもシェルを向上させます。
一般的なフレームワークとしては `prezto` や `oh-my-zsh` が有名ですが、`zsh-syntax-highlighting`や `zsh-history-substring-search` など特定の機能に特化した小さなフレームワークもあります。
`fish`のようなシェルには、これらのユーザーフレンドリーな機能の多くがデフォルトで含まれています。
これらの機能には次のようなものがあります。

- 右プロンプト
- コマンド構文の強調表示
- ヒストリー部分文字列検索
- manページベースのフラグ補完
- よりスマートなオートコンプリート
- プロンプトのテーマ


これらのフレームワークを使用する際の注意点は、実行するコードが適切に最適化されていなかったり、コード量が多すぎたりすると、シェルの速度が低下する可能性があることです。
頻繁に使用しない機能や、速度よりも重視する機能は、いつでもプロファイリングして無効にすることができます。

#ターミナルエミュレータ

シェルのカスタマイズと同時に、ターミナル・エミュレータの選択とその設定にも時間をかける価値があります。
世の中にはたくさんのターミナルエミュレータがあります（ここに比較表があります https://anarc.at/blog/2018-04-12-terminal-emulators-1/）

ターミナルには何百、何千時間も費やすことになるかもしれませんから、その設定を検討することは有益です。
ターミナルで変更したい点には次のようなものがあります。

- フォントの選択
- 配色
- キーボード ショートカット
- タブ/ペインのサポート
- スクロールバックの設定
- パフォーマンス（Alacrittyやkittyのような新しい端末では、GPUアクセラレーションを提供しているものもあります）

https://github.com/jwilm/alacritty
https://sw.kovidgoyal.net/kitty/

#練習問題
##ジョブの制御
1 . これまで見てきたところでは、`ps aux | grep` コマンドを使ってジョブの `pid` を取得してから `kill` することができますが、もっと良い方法があります。
ターミナルで`sleep 10000`のジョブを開始し、`Ctrl-Z`でバックグラウンドにし、`bg`で実行を継続します。
`pgrep`を使って`pid`を見つけ、`pkill`を使って`pid`を入力することなくそのジョブを殺すことができます。(ヒント: `-af` フラグを使用してください)。

2 . 他のプロセスが完了するまで、あるプロセスを開始したくないとしたら、どのようにすればよいでしょうか?
 この演習では、制限プロセスは常に `sleep 60 &` とします。そのためには、`wait`コマンドを使うのが一つの方法です。
`sleep`コマンドを起動して、バックグラウンドプロセスが終了するまで`ls`を待機させてみてください。
 ただし、`wait`は子プロセスに対してのみ動作するため、別の`bash`セッションで起動すると、この方法は失敗します。
ノートでは説明しなかった機能として、`kill`コマンドの終了ステータスは成功した場合は`0`、そうでない場合は`0`以外の値になります。
`kill-0`はシグナルを送信しませんが、プロセスが存在しない場合は`0`以外の終了ステータスを与えます。
`pid`を受け取り、与えられたプロセスが完了するまで待つ`pidwait`という`bash関数`を書いてください。
`CPU`を不必要に浪費しないように`sleep`を使うべきです。

##ターミナルマルチプレクサ
この`tmux`チュートリアルに沿って、以下の手順で基本的なカスタマイズを行う方法を学びます。
https://www.hamvocke.com/blog/a-quick-and-easy-guide-to-tmux/

##エイリアス
間違って`cd`と入力してしまったときのために、`cd`に解決するエイリアス`dc`を作成しておきます。

`history | awk '{$1="";print substr($0,2)}' | sort | uniq -c | sort -n | tail -n 10 `を実行して、よく使うコマンドのトップ10を取得し、それらのために短いエイリアスを書くことを検討します。

注意：これは`Bash`で動作します。`ZSH`を使用している場合は、単なる`history`ではなく、`history 1`を使用してください。

##ドットファイル
早速ですが、`dotfiles`を使ってみましょう。
	1 `dotfiles` のためのフォルダを作成し、バージョンコントロールを設定します。
	2 少なくとも 1 つのプログラム、例えばシェルに、カスタマイズした設定を追加します (最初は、`$PS1` を設定してシェルのプロンプトをカスタマイズするような単純なもので構いません)。
	3 新しいマシンにドットファイルを素早く(手動ではなく)インストールする方法を設定してください。これは、各ファイルに対して `ln -s` を呼び出すシェルスクリプトのような単純なものであってもよいですし、特殊なユーティリティを使用することもできます。
	4 新しい仮想マシン上で、インストールスクリプトをテストします。
	5 現在のツールの構成をすべて `dotfiles` リポジトリに移行します。
	6 `dotfiles` を `GitHub` で公開します。


##リモートマシン
この演習のために、Linux仮想マシンをインストールします（または既存の仮想マシンを使用します）。
仮想マシンに慣れていない方は、こちらのチュートリアルを参考にしてください。
https://hibbard.eu/install-ubuntu-virtual-box/
	1 `「~/.ssh/」`に移動し、そこに `SSH` 鍵のペアがあるかどうかを確認します。
ない場合は、`ssh-keygen -o -a 100 -t ed25519` で鍵を生成します。パスワードと `ssh-agent` の使用をお勧めします（詳細はこちら https://www.ssh.com/ssh/agent ）
	2 `.ssh/config` を編集し、以下のようなエントリを作成します。


```bash
Host vm
    User username_goes_here
    HostName ip_goes_here
    IdentityFile ~/.ssh/id_ed25519
    LocalForward 9999 localhost:8888
```

1 `ssh-copy-id vm` を使用して、`ssh` 鍵をサーバにコピーします。
2 `python -m http.server 8888` を実行して、`VM` 内で `Web` サーバを起動します。お使いのマシンで `http://localhost:9999` に移動して、`VM` ウェブサーバにアクセスします。
3 `sudo vim /etc/ssh/sshd_config` を実行して `SSH` サーバーの設定を編集し、`PasswordAuthentication` の値を編集してパスワード認証を無効にします。`PermitRootLogin`の値を編集して、ルートログインを無効にします。`sudo service sshd restart` で `ssh` サービスを再起動します。再度ログインしてみてください。
4 `(Challenge) VM` に `mosh` をインストールし、接続を確立します。その後、サーバ/VMのネットワークアダプタを切断します。`mosh`はそこからきちんと復旧できますか?
5 (課題) `ssh` で `-N と -f` フラグが何をするかを調べて、バックグラウンドのポートフォワーディングを実現するためのコマンドを考えてみてください。