Bash初心者からエキスパートになるためのコマンドとヒント101


以下は[Andrew](https://twitter.com/_awwsmm)による記事、[101 Bash Commands and Tips for Beginners to Experts](https://dev.to/awwsmm/101-bash-commands-and-tips-for-beginners-to-experts-30je)の日本語訳です。

一部を除き、上から順にコマンドを打って確かめることができるようになっています。
読むだけではなく、実際に打って試してみることで理解が早まることでしょう。


# 101 Bash Commands and Tips for Beginners to Experts

一年前まで、私はもっぱらMacOSとUbuntuのふたつのOSで作業をしていました。
両OSにおいて、私のデフォルトシェルはbashです。
過去6、7年ほどbashで仕事をしているため、bashがどのように動作するか、ある程度は理解しているつもりです。
従って、bashを始めたばかりの人にとって一般的で有用なコマンドについて、いくつか解説していきたいと思います。
また、bashについて知っているべきことを全て知っていると思っている人も、とりあえず見ていってください。
あなたが忘れているかもしれない、使うことで仕事がより簡単になるかもしれない、TIPSやリマインダのヒントを鏤めました。

以下のコマンドは段階を踏んで進んでいくので、bashを使い始めたばかりであっても、最初から最後まで順番に進んでいくことができます。
最初は簡単で、徐々に難しく一般的ではないコマンドを出していきます。


## The Basics

### First Commands, Navigating the Filesystem

最初のコマンドはファイルシステムのナビゲーション。

最近のファイルシステムはディレクトリツリー構造を持っています。
ディレクトリは親のないルートディレクトリ、もしくは一つの親を持つサブディレクトリのいずれかです。
ディレクトリツリーを逆方向(子から親へ)移動し続けると、最終的には必ずルートディレクトリに到達します。
一部のファイルシステムには複数のルートディレクトリを持つものもありますが(Windowsの`C:\`、`A:\`など)、UnixやUnixライクOSには単一のルートディレクトリ`/`しかありません。


#### pwd / ls / cd

ファイルシステム内で作業する場合、ユーザは常にどこかのディレクトリ、すなわちカレントディレクトリもしくは作業ディレクトリと呼ばれる場所にいます。
カレントディレクトリは`pwd`コマンドで確認することができます。

```bash
[ andrew@pc01 ~ ]$ pwd
/home/andrew
```

カレントディレクトリの中身、ファイルや子ディレクトリなどを参照するのは`ls`コマンドです。

```bash
[ andrew@pc01 ~ ]$ ls
Git  TEST  jdoc  test  test.file
```

> おまけ
> `ls -a`で隠しファイル(.で始まるなど)を表示する。
> `ls -l`でファイルの詳細も表示する。
> `ls -l -a`のように複数のオプションを連ねられる。
> `ls -l -a`のかわりに`ls -la`と書くこともできる。

カレントディレクトリを変更するのは`cd`コマンドです。

```bash
[ andrew@pc01 ~ ]$ cd TEST/

[ andrew@pc01 TEST ]$ pwd
/home/andrew/TEST

[ andrew@pc01 TEST ]$ cd A

[ andrew@pc01 A ]$ pwd
/home/andrew/TEST/A
```

`cd ..`で親ディレクトリに移動することができます。

```bash
[ andrew@pc01 A ]$ cd ..

[ andrew@pc01 TEST ]$ pwd
/home/andrew/TEST
```

`cd ~`あるいは単に`cd`で、どこからでもユーザのホームディレクトリ(通常は`/home/username`)に戻ってきます。

```bash
[ andrew@pc01 TEST ]$ cd

[ andrew@pc01 ~ ]$ pwd
/home/andrew
```

> おまけ
> `cd ~user`は、"userのホームディレクトリにcd"という意味。
> `cd ../..`のように一気に多段ディレクトリ移動ができる。
> ひとつ前にいたディレクトリに戻るのは`cd -`。
> `.`はカレントディレクトリのこと。よって`cd .`は何もしない。

#### ; / && / &

コマンドラインに入力するのはコマンドと呼ばれるもので、そしてコマンドはPCのどこかに保存されているプログラムを実行させます。
このプログラムはLinuxのビルトインコマンドであることもあれば、誰かが作ったアプリであることもあり、そしてあなたが作ったコードであるかもしれません。
時折、複数のコマンドを連続して実行したいことがあります。
そのためにはセミコロン`;`を使います。

```bash
[ andrew@pc01 ~ ]$ ls; pwd
Git  TEST  jdoc  test  test.file
/home/andrew
```

上のコマンドは、まず`ls`でカレントディレクトリの中身を表示し、次いで`pwd`でカレントディレクトリの位置を表示します。

コマンドを連続実行するためのもうひとつ便利なツールが`&&`です。
これを使うと、左側のコマンドが失敗した場合は右のコマンドが実行されません。
`;`や`&&`は連続で使うことができます。

```bash
# cdでtypoしたのでpwd以降は実行されない
[ andrew@pc01 ~ ]$ cd /Giit/Parser && pwd && ls && cd
-bash: cd: /Giit/Parser: No such file or directory

# 成功したので後続のコマンドも実行される
[ andrew@pc01 ~ ]$ cd Git/Parser/ && pwd && ls && cd
/home/andrew/Git/Parser
README.md  doc.sh  pom.xml  resource  run.sh  shell.sh  source  src  target
```

`;`を使った場合は、手前のコマンドが失敗したとしても後ろのコマンドが実行されます。

```bash
# pwdに失敗したけどpwdやlsが動く
[ andrew@pc01 ~ ]$ cd /Giit/Parser ; pwd ; ls
-bash: cd: /Giit/Parser: No such file or directory
/home/andrew
Git  TEST  jdoc  test  test.file
```

`&`は一見`&&`と似たように見えますが、実際は全く異なる機能です。
実行に長い時間がかかるコマンドを実行すると、そのコマンドの実行が終了するまでコマンドラインは何もできなくなります。
コマンドの後ろに`&`を書くと待ちが発生しなくなり、古いコマンドがまだ動いている状態でも、次のコマンドを実行することができるようになります。

```bash
[ andrew@pc01 ~ ]$ cd Git/Parser && mvn package & cd
[1] 9263
```

> おまけ
> `&`を使ってコマンドを隠すことを、我々はジョブ(あるいはプロセス)のバックグラウンド化と呼んでいる。
> 現在バックグラウンド状態のジョブは`jobs`コマンドで確認することができる。

```bash
[ andrew@pc01 ~ ]$ jobs
[1]+ Running cd Git/Parser/ && mvn package &
```
### Getting Help

#### -h

ほとんどのコマンドは、`-h`あるいは`--help`でコマンドのヘルプを表示してくれます。

```bash
[ andrew@pc01 ~ ]$ du --help
Usage: du [OPTION]... [FILE]...
  or:  du [OPTION]... --files0-from=F
Summarize disk usage of the set of FILEs, recursively for directories.

Mandatory arguments to long options are mandatory for short options too.
  -0, --null            end each output line with NUL, not newline
  -a, --all             write counts for all files, not just directories
      --apparent-size   print apparent sizes, rather than disk usage; although
                          the apparent size is usually smaller, it may be
                          larger due to holes in ('sparse') files, internal
                          fragmentation, indirect blocks, and the like
  -B, --block-size=SIZE  scale sizes by SIZE before printing them; e.g.,
                           '-BM' prints sizes in units of 1,048,576 bytes;
                           see SIZE format below
...
```

#### man

大抵のコマンドは、`man`コマンドに続けて打つとコマンドのマニュアルを表示することができます。
`q`でマニュアルを終了します。

```bash
LS(1)                            User Commands                           LS(1)

NAME
       ls - list directory contents

SYNOPSIS
       ls [OPTION]... [FILE]...

DESCRIPTION
       List  information  about  the FILEs (the current directory by default).
       Sort entries alphabetically if none of -cftuvSUX nor --sort  is  speci-
       fied.

       Mandatory  arguments  to  long  options are mandatory for short options
       too.
...
```

### Viewing and Editing Files

#### head / tail / cat / less

`head`はファイルの最初の数行を出力します。
出力行数のデフォルトは10で、`-n`で変更できます。

```bash
# 最初の3行を表示
[ andrew@pc01 ~ ]$ head -n 3 c
this
file
has
```

`tail`は逆にファイルの最後の数行を出力します。
出力行数は`head`と同じように指定するか、もしくは`-n +N`でN行目から最後までを出力できます。

```bash
# 4行目から最後までを表示
[ andrew@pc01 ~ ]$ tail -n +4 c
exactly
six
lines
```

`cat`は複数のファイルを連結し、その結果を標準出力(通常はターミナル)に送信します。
しかしこのコマンドは、複数のファイルや、単に1ファイルの中身をさっと確認するために使われることがしばしばあります。
そのような使い方をすると[猫の無駄遣い](http://bit.ly/2SPHE4V)で告発される可能性がありますが、たいした害はないので気にしなくてもかまいません。

```bash
[ andrew@pc01 ~ ]$ cat a
file a

[ andrew@pc01 ~ ]$ cat a b
file a
file b
```

ファイルを素早く表示するもうひとつの方法が`less`で、これを使うと読み取り専用の`vim`のようなウィンドウが開きます。
実は`more`というコマンドもあるのですが、`less`のほうが優位なので出番はありません。
もっと詳しく(more)、いや少なく？(less)知りたい場合は、[less](http://man7.org/linux/man-pages/man1/less.1.html)と[more](http://man7.org/linux/man-pages/man1/more.1.html)のmanページを参照してください。

#### nano / nedit

`nano`は必要最小限のコマンドラインテキストエディタです。
初心者や、100万のショートカットを覚えたくないユーザにとっては最適の優れたエディタです。
私のキャリアにおいても、最初の数年間は`nano`だけで十分でした。
`nano`で独自のシンタックスハイライトを定義するのは大変なので、さすがにそろそろ別の強力なエディタを検討し始めていますが。

`nedit`は小規模なグラフィカルテキストエディタで、X Windowを開き、クリック編集、ドラッグアンドドロップ、構文強調表示等が可能です。
スクリプトに小さな変更を加えながら何度も実行したいとき、私はよく`nedit`を使用します。

その他の一般的なCLI/GUIテキストエディタとして、`emacs`、`vi`、`vim`、`gedit`、`Notepad++`、`Atom`、そしてその他たくさんの選択肢が存在します。
私が触った中でも特に魅力的で推奨できると感じたものは、`Micro`、`Light Table`、そして`VS Code`です。

最近のエディタは全て、検索、置換、構文強調表示などの便利機能を標準装備しています。
`vi`や`emacs`は`nano`や`nedit`より遙かに多くの機能がありますが、同時に学習曲線も急坂です。
エディタを色々試してみて、自分に合ったものを探してみてください。

### Creating and Deleting Files and Directories

ファイル・ディレクトリの作成と削除。

#### touch

`touch`はファイルのタイムスタンプを更新するために作られたものですが、空のファイルを作るためにも利用できます。
`nano`などのテキストエディタで新規ファイルを作成してみましょう。

```bash
[ andrew@pc01 ex ]$ ls

[ andrew@pc01 ex ]$ nano a

/* ここで保存してエディタを終了 */

[ andrew@pc01 ex ]$ ls
a
```

`touch`なら1コマンドです。

```bash
[ andrew@pc01 ex ]$ touch b && ls
a  b
```

> おまけ
> `Ctrl+z`で現在実行中のプロセスをバックグラウンドにできる。
> `fg`コマンドでプロセスに戻る。

```bash
[ andrew@pc01 ex ]$ nano a

/* ここでCtrl + z */

Use fg to return to nano

[1]+ Stopped nano a
[ andrew@pc01 ex ]$ fg

/* 編集画面に戻った */
```

> さらにおまけ
> `Ctrl+c`だと現在実行中のプロセスを強制終了する。
> バックグラウンドプロセスを強制終了するには`kill %N`。
> Nに入れるプロセス番号は`jobs`コマンドで確認できる。


#### mkdir / rm / rmdir

空のディレクトリを作るのは`mkdir`コマンドです。

```bash
[ andrew@pc01 ex ]$ ls && mkdir c && ls
a  b
a  b  c
```

`rm`でファイルを削除できますが、削除すると元に戻せません。
注意してください。

```bash
[ andrew@pc01 ex ]$ rm a && ls
b  c
```

削除確認の警告を行いたい場合は`-i`オプションを追加しましょう。

```bash
[ andrew@pc01 ex ]$ rm -i b
rm: remove regular empty file 'b'? y
```

空のディレクトリを削除するのは`rmdir`です。
空のディレクトリで`ls -a`すると、カレントディレクトリ`.`と親ディレクトリ`..`だけが表示されます。

```bash
[ andrew@pc01 ex ]$ rmdir c && ls -a
.  ..
```

`rmdir`では、空でないディレクトリは削除できません。

```bash
[ andrew@pc01 ex ]$ cd .. && ls test/
*.txt  0.txt  1.txt  a  a.txt  b  c

[ andrew@pc01 ~ ]$ rmdir test/
rmdir: failed to remove 'test/': Directory not empty
```

中身の入ったディレクトリを全て強制的に削除するには、`rm -rf`を使います。
`-r`は再帰、`-f`は強制実行するオプションです。

```bash
[ andrew@pc01 ~ ]$ rm –rf test
```

### Moving and Copying Files, Making Links, Command History

ファイルの移動、コピー、リンク、コマンド履歴。

#### mv / cp / ln

ファイルの移動、リネームは`mv`です。
ディレクトリを移動してファイル名をそのままにする、ファイル名もついでに変更するといったことができます。

```bash
[ andrew@pc01 ex ]$ ls && mv a e && ls
a  b  c  d
b  c  d  e
```

コピーは`cp`です。

```bash
[ andrew@pc01 ex ]$ cp e e2 && ls
b  c  d  e  e2
```

`ln`はファイルへのハードリンクを作成します。

```bash
# 第一引数が対象ファイル、第二引数がリンク名
[ andrew@pc01 ex ]$ ln b f && ls
b  c  d  e  e2  f
```

`ln -s`でシンボリックリンクが作成できます。

```bash
[ andrew@pc01 ex ]$ ln -s b g && ls
b  c  d  e  e2  f  g
```

ハードリンクは同じメモリを参照するひとつのファイルへの別名を作ります。
シンボリックリンクは元のファイル名を追跡するだけで、ファイル実体を参照するのは元ファイルだけです。
これ以上の詳細については[What is the difference between a symbolic link and a hard link?](https://stackoverflow.com/questions/185899/what-is-the-difference-between-a-symbolic-link-and-a-hard-link)を見てください。

#### Command History

bashは、実行したコマンドを再実行するために役立つ機能をふたつ持っています。
ひとつめの機能はタブ補完です。
コマンドの一部を入力したところでTABを押すと、何を打とうとしているのか端末が推測してくれます。

```bash
[ andrew@pc01 dir ]$ ls <ENTER>
anotherlongfilename  thisisalongfilename  anewfilename

[ andrew@pc01 dir ]$ ls t <TAB>
```

TABを押すと、以下のようにコマンドが最後まで補完されます。

```bash
[ andrew@pc01 dir ]$ ls thisisalongfilename <ENTER>
thisisalongfilename
```

特定しきれない場合はTABを何度か押すことになります。

```bash
[ andrew@pc01 dir ]$ ls a <TAB>

[ andrew@pc01 dir ]$ ls an <TAB>
anewfilename  anotherlongfilename
```

bashは以前に実行したコマンドを幾つか覚えており、`Ctrl+R`でその履歴を検索することができます。

```bash
(reverse-i-search)`':
```

ここで`anew`と入力すると、その文字が含まれるコマンドのうち最後に実行したものが出てきます。

```bash
(reverse-i-search)`anew': touch anewfilename
```

### Directory Trees, Disk Usage, and Processes

ディレクトリツリー、ディスク使用量、プロセス。

#### mkdir –p / tree

`mkdir`はデフォルトではひとつのディレクトリのみを作成します。
どういうことかというと、ディレクトリ`d/e`が存在しないときにディレクトリ`d/e/f`を作ることはできないということです。

```bash
[ andrew@pc01 ex ]$ ls && mkdir d/e/f
a  b  c
mkdir: cannot create directory 'd/e/f': No such file or directory
```

`-p`オプションを付けることで、深い階層のディレクトリも一気に作成してくれます。

```bash
[ andrew@pc01 ex ]$ mkdir -p d/e/f && ls
a  b  c  d
```

`tree`コマンドはディレクトリツリーをいいかんじにビジュアル化して表示してくるので、ディレクトリ構造を把握するのに役立ちます。
デフォルトでは指定ディレクトリ以下のディレクトリツリーを全て示しますが、`-L`オプションで階層を限定することもできます。

```bash
[ andrew@pc01 ex ]$ tree -L 2
.
|-- a
|-- b
|-- c
`-- d
    `--e

3 directories, 2 files
```

空のディレクトリを表示させたくないときは`--prune`オプションを使います。
このオプションは再帰的なので、空のディレクトリだけが入った空でないディレクトリも消えることに注意しましょう。

```bash
[ andrew@pc01 ex ]$ tree --prune
.
|-- a
`-- b
```

#### df / du / ps

`df`はディスクおよびシステムの容量がどれだけ使用されているかを一覧表示します。

```bash
[ andrew@pc01 ex ]$ df -h
Filesystem                   Size  Used Avail Use% Mounted on
udev                         126G     0  126G   0% /dev
tmpfs                         26G  2.0G   24G   8% /run
/dev/mapper/ubuntu--vg-root  1.6T  1.3T  252G  84% /
...
```

このコマンドにおいては、`-h`オプションは"ヘルプ表示"ではなく"人間が見てわかりやすく表示"を意味します。
いくつかのコマンドは、このオプションを使うことで、バイト数そのままではなくキロバイトK、ギガバイトGなどでファイルやディスク容量を出力してくれます。
`du`は指定ディレクトリとそのサブディレクトリの使用量を表示します。
あるハードディスクの使用量を知りたい場合は`df`、あるディレクトリの使用量を知りたいときは`du`を使います。

```bash
[ andrew@pc01 ex ]$ du
4       ./d/e/f
8       ./d/e
12      ./d
4       ./c
20      .
```

`du`は`--max-depth=N`オプションを受け取り、指定した階層のディレクトリまでを表示します。

```bash
[ andrew@pc01 ex ]$ du -h --max-depth=1
12K     ./d
4.0K    ./c
20K     .
```

`ps`はユーザが実行中の全てのプロセス(ジョブ)を表示します。

```bash
[ andrew@pc01 ex ]$ ps
  PID TTY          TIME CMD
16642 pts/15   00:00:00 ps
25409 pts/15   00:00:00 bash
```

### Miscellaneous

#### passwd / logout / exit

パスワード変更コマンドは`passwd`です。
確認のために現在のコマンドを一回と、変更後のパスワードを2回入力する必要があるため、入力ミスすることはないでしょう。

```bash
[ andrew@pc01 dir ]$ passwd
Changing password for andrew.
(current) UNIX password:    <現在のパスワード>
Enter new UNIX password:    <新パスワード>
Retype new UNIX password:   <新パスワードを再入力>
passwd: password updated successfully
```

`logout`で現在のログインシェルが終了します。

```bash
[ andrew@pc01 dir ]$ logout

──────────────────────────────────────────────────────────────────────────────
Session stopped
    - Press <return> to exit tab
    - Press R to restart session
    - Press S to save terminal output to file
```

`exit`はサブシェルを含め全て終了します。

```bash
[ andrew@pc01 ~ ]$ exit
logout

──────────────────────────────────────────────────────────────────────────────
Session stopped
    - Press <return> to exit tab
    - Press R to restart session
    - Press S to save terminal output to file
```

#### clear / *

`clear`を実行するとカーソルが画面の一番上に移動します。
実際やってることは現在の行の下に空白行を追加しているだけですが、ワークスペースをクリアするのに有用です。

ファイルの検索にはglob(*、ワイルドカード、Kleene Star)が便利です。
以下の2コマンドの違いについて注意してください。

```bash
[ andrew@pc01 ~ ]$ ls Git/Parser/source/
PArrayUtils.java     PFile.java            PSQLFile.java      PWatchman.java
PDateTimeUtils.java  PFixedWidthFile.java  PStringUtils.java  PXSVFile.java
PDelimitedFile.java  PNode.java            PTextFile.java     Parser.java

[ andrew@pc01 ~ ]$ ls Git/Parser/source/PD*
Git/Parser/source/PDateTimeUtils.java  Git/Parser/source/PDelimitedFile.java
```

globは1コマンド中に何度も使用することができ、"0文字以上の文字列"に一致します。

```bash
[ andrew@pc01 ~ ]$ ls Git/Parser/source/P*D*m*
Git/Parser/source/PDateTimeUtils.java  Git/Parser/source/PDelimitedFile.java
```

## Intermediate

### Disk, Memory, and Processor Usage

#### ncdu

`ncdu`(NCurses Disk Usage)はファイル使用量の概要を表示する、改善版`du`のようなコマンドです。
読み取り専用の`vim`のようなウィンドウが開くので、終了する際は`q`を押しましょう。

```bash
[ andrew@pc01 ~ ]$ ncdu

ncdu 1.11 ~ Use the arrow keys to navigate, press ? for help
--- /home/andrew -------------------------------------------------------------
  148.2 MiB [##########] /.m2
   91.5 MiB [######    ] /.sbt
   79.8 MiB [#####     ] /.cache
   64.9 MiB [####      ] /.ivy2
   40.6 MiB [##        ] /.sdkman
   30.2 MiB [##        ] /.local
   27.4 MiB [#         ] /.mozilla
   24.4 MiB [#         ] /.nanobackups
   10.2 MiB [          ]  .confout3.txt
    8.4 MiB [          ] /.config
    5.9 MiB [          ] /.nbi
    5.8 MiB [          ] /.oh-my-zsh
    4.3 MiB [          ] /Git
    3.7 MiB [          ] /.myshell
    1.7 MiB [          ] /jdoc
    1.5 MiB [          ]  .confout2.txt
    1.5 MiB [          ] /.netbeans
    1.1 MiB [          ] /.jenv
  564.0 KiB [          ] /.rstudio-desktop
 Total disk usage: 552.7 MiB  Apparent size: 523.6 MiB  Items: 14618
```

#### top / htop

`top`は、現在実行されている全てのプロセスと所有者、メモリ使用量などを表示します。
そして`htop`はインタラクティブな改良版`top`です。
いずれも`-u username`オプションで該当ユーザのプロセスだけを表示することができます。

### REPLs and Software Versions

#### REPLs

REPLは`Read-Eval-Print Loop`の略で、コマンドラインと同じようなものですが、通常はプログラミング言語との対話的ウィンドウに対して呼ばれます。
PythonのREPLは`python`コマンドで始めることができ、`quit()`関数で終了します。

```bash
[ andrew@pc01 ~ ]$ python
Python 3.5.2 (default, Nov 12 2018, 13:43:14) ...
>>> quit()
```

RのREPLは`R`コマンドで始まり、`q()`関数で終了です。

```bash
[ andrew@pc01 ~ ]$ R
R version 3.5.2 (2018-12-20) --"Eggshell Igloo" ...
> q()
Save workspace image? [y/n/c]: n
```

ScalaのREPLは`scala`コマンドで始まり、`quit`コマンドで終了です。

```bash
[ andrew@pc01 ~ ]$ scala
Welcome to Scala 2.11.12 ...
scala> :quit
```

JavaのREPLは`jshell`コマンドで始まり、`/exit`コマンドで終了です。

```bash
[ andrew@pc01 ~ ]$ jshell
| Welcome to JShell--Version 11.0.1 ...
jshell> /exit
```

これらのREPLは`Ctrl+d`でも終了できます。
`Ctrl+d`はUnixのEOFであり、入力の終端を意味します。

#### -version / --version / -v

ほとんどのコマンドやプログラムは、そのソフトウェアバージョンを表示する`-version`もしくは`--version`オプションがあります。
ほとんどのアプリケーションにも同じオプションが用意されています。

```bash
[ andrew@pc01 ~ ]$ ls --version
ls (GNU coreutils) 8.25 ...

[ andrew@pc01 ~ ]$ ncdu -version
ncdu 1.11

[ andrew@pc01 ~ ]$ python --version
Python 3.5.2
```

ただし、稀にあまり馴染みのないオプションも存在します。

```bash
[ andrew@pc01 ~ ]$ sbt scalaVersion
...
[info] 2.12.4
```

バージョンを表すオプションとして`-v`を使うプログラムもあります。
しかし多くの場合`-v`は'verbose'を意味し、診断情報やデバッグ情報といった多くの詳細情報も出力します。

```bash
SCP(1)                    BSD General Commands Manual                   SCP(1)

NAME
     scp -- secure copy (remote file copy program)
...
-v      Verbose mode.  Causes scp and ssh(1) to print debugging messages
             about their progress.  This is helpful in debugging connection,
             authentication, and configuration problems.
...
```

### Environment Variables and Aliases

#### Environment Variables

環境変数("env vars"と略されることもある)は、`bash`シェル上で作成・使用できる永続変数のことです。
これらは`=`で定義し、`$`で参照します。
`pritenv`で現在定義されている全ての環境変数を確認することができます。

```bash
[ andrew@pc01 ~ ]$ printenv
SPARK_HOME=/usr/local/spark
TERM=xterm
...
```

新しい環境変数を定義しましょう。
`=`の前後にスペースを入れてはいけません。

```bash
[ andrew@pc01 ~ ]$ myvar=hello
```

定義した環境変数は`echo`で取得できます。
ただし変数の頭に`$`を付けます。

```bash
[ andrew@pc01 ~ ]$ echo $myvar
hello
```

スペースやその他の空白を含む値を指定したいときは、値を`"`で囲まなければなりません。
また環境変数は警告無しに上書きできるので気をつけましょう。

```bash
[ andrew@pc01 ~ ]$ myvar="hello, world!" && echo $myvar
hello, world!
```

環境変数を`export`コマンド付きで定義することもできます。
そうすると、サブシェル内でもその環境変数を参照することができるようになります。

```bash
[ andrew@pc01 ~ ]$ export myvar="another one" && echo $myvar
another one
```

環境変数の設定を解除するには、`=`の右側を空欄にして決定するか、`unset`コマンドを使用します。

```bash
[ andrew@pc01 ~ ]$ unset mynewvar

[ andrew@pc01 ~ ]$ echo $mynewvar
```

#### Aliases

エイリアスは環境変数に似ていますが、普通は異なる目的で利用されます。
長いコマンドを短く置き換えるために使用されます。

```bash
[ andrew@pc01 apidocs ]$ ls -l -a -h -t
total 220K
drwxr-xr-x 5 andrew andrew 4.0K Dec 21 12:37 .
-rw-r--r-- 1 andrew andrew 9.9K Dec 21 12:37 help-doc.html
-rw-r--r-- 1 andrew andrew 4.5K Dec 21 12:37 script.js
...

[ andrew@pc01 apidocs ]$ alias lc="ls -l -a -h -t"

[ andrew@pc01 apidocs ]$ lc
total 220K
drwxr-xr-x 5 andrew andrew 4.0K Dec 21 12:37 .
-rw-r--r-- 1 andrew andrew 9.9K Dec 21 12:37 help-doc.html
-rw-r--r-- 1 andrew andrew 4.5K Dec 21 12:37 script.js
...
```

エイリアスの削除は`unalias`です。

```bash
[ andrew@pc01 apidocs ]$ unalias lc

[ andrew@pc01 apidocs ]$ lc
The program 'lc' is currently not installed. ...
```

> おまけ
> [環境変数とエイリアスの違い](https://stackoverflow.com/questions/7342735/bash-command-whats-the-difference-between-a-variable-and-an-alias)。
> 一部のプログラムでは、そのプログラム専用のエイリアスを設定できる。[gitの例](https://git-scm.com/book/en/v2/Git-Basics-Git-Aliases)。

### Basic bash Scripting

#### bash Scripts

bashスクリプト(シェルスクリプト)を使うと、複雑なプロセスを自動化し、再利用可能な形にパッケージ化することができます。
拡張子は通常`.sh`です。
bashスクリプトには、通常のコマンドをいくらでも詰め込むことができます。

```bash
[ andrew@pc01 ~ ]$ echo "ls && touch file && ls" > ex.sh
```

作成したシェルスクリプトは、`source`もしくは`sh`コマンドで実行可能です。

```bash
[ andrew@pc01 ~ ]$ source ex.sh
Desktop  Git  TEST  c  ex.sh  project  test
Desktop  Git  TEST  c  ex.sh  file  project  test
```

`chmod`コマンドで、シェルスクリプトを直接実行可能にすることができます。
詳しくは後で解説します。

```bash
[ andrew@pc01 ~ ]$ echo "ls && touch file2 && ls" > ex2.sh

[ andrew@pc01 ~ ]$ chmod +x ex2.sh
```

実行権限`+x`のついたシェルスクリプトは、頭に`./`を付けることで直接実行できます。

```bash
[ andrew@pc01 ~ ]$ ./ex2.sh
Desktop  Git  TEST  c  ex.sh  ex2.sh  file  project  test
Desktop  Git  TEST  c  ex.sh  ex2.sh  file  file2  project  test
```

長いスクリプトを書きたい場合、行の最後に`\`を入れることで改行して入力できます。

```bash
[ andrew@pc01 ~ ]$ echo "for i in {1..3}; do echo \
> \"Welcome \$i times\"; done" > ex3.sh
```

シェルスクリプトにはもちろん、ループや関数などの複雑な構造も仕込むことができます。

```bash
[ andrew@pc01 ~ ]$ source ex3.sh
Welcome 1 times
Welcome 2 times
Welcome 3 times
```

#### Custom Prompt and ls

bashスクリプティングは、あなたの人生をずっと楽に、豊かに、カラフルにしてくれます。
この素晴らしい[チートシート](https://devhints.io/bash)をご覧ください。

ところでシェルのプロンプトに出てくる文字列も環境変数で定義されていて、その名前は`$PS1`です。
他にもプロンプトはあって、それらについては[こちら](http://bit.ly/2SPgsmT)を見てください。

```bash
[ andrew@pc01 ~ ]$ printf "%q" $PS1
$'\\n\\[\E[1m\\]\\[\E[30m\\]\\A'$'\\[\E[37m\\]|\\[\E[36m\\]\\u\\[\E[37m\\]@\\[\E[34m\\]\\h'$'\\[\E[32m\\]\\W\\[\E[37m\\]|'$'\\[\E(B\E[m\\]‘
```

デフォルトのプロンプトは、`export`コマンドで変更可能です。

```bash
[ andrew@pc01 ~ ]$ export PS1="\ncommand here> "

command here> echo $PS1
\ncommand here>
```

[色を付ける](http://bit.ly/2TMbEit)こともできます。


```bash
command here> export PS1="\e[1;31m\nCODE: \e[39m"

# (ここ本当は赤いんだけどMarkdownで色の付け方がわからんかった)
CODE: echo $PS1
\e[1;31m\nCODE: \e[39m
```

`ls`が出力する色も環境変数`$LS_COLORS`で変更することができます。

```bash
CODE: ls
Desktop  Git  TEST  c  ex.sh  ex2.sh  ex3.sh  file  file2  project  test

CODE: export LS_COLORS='di=31:fi=0:ln=96:or=31:mi=31:ex=92'

# (ここも本当は赤い)
CODE: ls
Desktop  Git  TEST  c  ex.sh  ex2.sh  ex3.sh  file  file2  project  test
```

### Config Files

#### Config Files / .bashrc

色々とコマンドを実行して設定を変更しましたが、いちどログアウトして再度ログインすると、全ての変更がリセットされてしまうことに気付くでしょう。
設定ファイルを使用することで、ログインしなおしてもシェルや特定のプログラムの設定を保持することができます。
bashシェルの主な設定ファイルは`~/.bashrc`です。
`~/.bashrc`に追加したエイリアス、環境変数、エイリアスは、ログインするたびに使用可能になります。
`~/.bashrc`に書かれたコマンドはログイン時に実行されるということです。
`~/.bashrc`を更新した場合、`source`コマンドでログアウトせずに最新状態にすることができます。

```bash
[ andrew@pc01 ~ ]$ nano ~/.bashrc
```

そして最初に`echo "~/.bashrc loaded!"`という行を付け加えてみましょう。

```bash
[ andrew@pc01 ~ ]$ source ~/.bashrc
~/.bashrc loaded!
```

ログアウトしてログインし直してもこうなります。

```bash
Last login: Fri Jan 11 10:29:07 2019 from 111.11.11.111
~/.bashrc loaded!

[ andrew@pc01 ~ ]
```


#### Types of Shells

ログインシェルとは、ログインしたとき最初に起動するシェルです。
対話型シェルとは、コマンドを入力することができるシェルです。
シェルには対話型ログインシェル、非ログイン非対話型シェル、あるいは他の組み合わせなど様々なものがあります。

`~/.bashrc`以外にも、ログインしたときやログアウトした際に自動的に実行されるスクリプトは幾つか存在します。
例を挙げると、

・`/etc/profile`
・`~/.bash_profile`
・`~/.bash_login`
・`~/.profile`
・`~/.bash_logout`
・`/etc/bash.bash_logout`

これらのスクリプトのどれが有効で、どの順番で実行されるかは、シェルのタイプによって異なります。
詳細については[bashのmanページ](https://linux.die.net/man/1/bash)や、[Stack](http://bit.ly/2TGCwA8) [Overflow](http://bit.ly/2TFHFsf)のポストなどを参照してください。

bashは`source`コマンドで別のスクリプトを読み込むことも可能です。
たとえば`~/.bashrc`に以下を記載することができます。

```bash
source ~/.bashrc_addl
```

これにより、`.bashrc_addl`も`source`対象のファイルとなります。
このファイルにも独自のエイリアス、関数、環境変数などを記述することができます。
同様に、さらに別のスクリプトを`source`することもできます。
そうする場合は`source`が無限ループしないように注意してください。

機能やOS(Ubuntu、RedHat、macOS)などによってコマンドをファイルごとに分離しておくと役立つことがあります。

・`~/.bash_ubuntu` …Ubuntu固有の設定
・`~/.bashrc_styles` …`PS1`や`LS_COLORS`など外観の設定
・`~/.bash_java` …言語ごとのの設定

外観やOS固有設定などをそれぞれ個別のファイルに切り出し、そしてそれらのうち必要なものを`source`するひとつの管理ファイルから構成します。
このような構成であれば、あらゆるマシンやOSで使用可能なシェルになります。

bash以外のシェルも存在することに気をつけてください。
bashは単にシェルの一種であるにすぎません。
他に一般的なシェルとしては`zsh`、`csh`、`fish`などが存在します。
様々なシェルを試して、自分に合ったものを見つけてください。

ただし、このチュートリアルはbashシェルで記述されているため、他のシェルでは動作しない可能性があることに注意してください。

### Finding Things

#### whereis / which / whatis

`whereis`は、あるコマンドに対して、おそらく有用であろうファイルを検索します。
そのコマンドのバイナリすなわち実行可能なコード、ソースファイル、マニュアルのmanページなどです。

```bash
[ andrew@pc01 ~ ]$ whereis ls
ls: /bin/ls /usr/share/man/man1/ls.1.gz
```

`which`はバイナリ、すなわちコマンドの場所のみを返します。

```bash
[ andrew@pc01 ~ ]$ which ls
/bin/ls
```

`whatis`はmanページからコマンドの概要を1行表示します。

```bash
[ andrew@pc01 ~ ]$ whatis whereis which whatis
whereis (1)          - locate the binary, source, and manual page files for a command
which (1)            - locate a command
whatis (1)           - display one-line manual page descriptions
```

`which`は、エイリアスで隠蔽されているコマンドのオリジナルを探すのに役立ちます。

```bash
[ andrew@pc01 ~ ]$ alias ls="ls -l"

# エイリアスのせいで元コマンドと挙動が違う
[ andrew@pc01 ~ ]$ ls
total 36
drwxr-xr-x 2 andrew andrew 4096 Jan  9 14:47 Desktop
drwxr-xr-x 4 andrew andrew 4096 Dec  6 10:43 Git
...

# whichで調べた元コマンドを直接叩けばオリジナルの結果が得られる
[ andrew@pc01 ~ ]$ /bin/ls
Desktop  Git  TEST  c  ex.sh  ex2.sh  ex3.sh  file  file2  project  test
```

#### locate / find

`locate`は、予め作成しておいたキャッシュリストを参照して高速にファイルを検索します。

```bash
[ andrew@pc01 ~ ]$ locate README.md
/home/andrew/.config/micro/plugins/gotham-colors/README.md
/home/andrew/.jenv/README.md
/home/andrew/.myshell/README.md
...
```

リストを検索するので`find`より高速ですが、必ずしも最新の状態と一致しているとは限りません。
`find`は実際のファイルシステムを検索して、探しているファイルを見つけます。
こちらは実ファイルを検索するため、常に最新のファイルを得ることができます。

```bash
[ andrew@pc01 ~ ]$ find ~/ -iname "README.md"
/home/andrew/.jenv/README.md
/home/andrew/.config/micro/plugins/gotham-colors/README.md
/home/andrew/.oh-my-zsh/plugins/ant/README.md
...
```

`find`は1971年という最初期のUNIXから実装されているため、1994年にGNUに追加された`locate`より遙かに広い環境で利用可能です。

`find`は`locate`よりずっと多くの機能を備えていて、更新日、サイズ、所有者、ファイル/ディレクトリ、タイムスタンプ、パーミッション、ディレクトリの深さなど多様な条件で検索できます。
またファイル名を正規表現で検索し、見つかったファイルに対してコマンドを実行することもできます。

高速に検索する必要がある場合、また対象ファイルが何処にあるかわからないときは`locate`を使用します。
ファイル名以外の何らかの条件に紐付いた正確なファイル一覧が欲しい場合、取得したファイルに対して何かを行いたいときは`find`を使用します。

### Downloading Things

#### ping / wget / curl

`ping`はネットワークホストとの通信を確立しようとします。
主にインターネット接続がダウンしているかどうかを確認するために使用されます。

```bash
[ andrew@pc01 ~ ]$ ping google.com
PING google.com (74.125.193.100) 56(84) bytes of data.
Pinging 74.125.193.100 with 32 bytes of data:
Reply from 74.125.193.100: bytes=32 time<1ms TTL=64
...
```

`wget`はインターネットからファイルを簡単にダウンロードできます。

```bash
[ andrew@pc01 ~ ]$ wget \
> http://releases.ubuntu.com/18.10/ubuntu-18.10-desktop-amd64.iso
```

`curl`も`wget`と同じように使用できます。
`--output`オプションを忘れないようにしてください。

```bash
[ andrew@pc01 ~ ]$ curl \
> http://releases.ubuntu.com/18.10/ubuntu-18.10-desktop-amd64.iso \
> --output ubuntu.iso
```

`curl`と`wget`には、それぞれ長所と短所があります。
`curl`はより多くのプロトコルをサポートし、様々な場面で使用可能です。
`curl`はデータの送信もできますが、`wget`は受信しかできません。
`wget`はファイルを再帰的にダウンロードできますが、`curl`はできません。

一般的には、インターネットからファイルをダウンロードするときは`wget`を使います。
`curl`を使ってデータを送信することはあまりありませんが、これを知っておくと、いざ必要になったときに役立ちます。

#### apt / gunzip / tar / gzip

Debian系のLinuxディストリビューションには、`apt`という素晴らしいパッケージ管理ツールが存在します。
ソフトウェアのインストール、アップデート、削除が可能です。
何らかのソフトウェアが必要になった場合、`apt search`で検索し、`apt install`でインストールします。

```bash
[ andrew@pc01 ~ ]$ apt search bleachbit
...bleachbit/bionic,bionic 2.0-2 all
  delete unnecessary files from the system

# インストールにはsudoが必要
[ andrew@pc01 ~ ]$ sudo apt install bleachbit
```

Linuxのソフトウェアは大抵tarball(.tar.gz)になっています。

```bash
[ andrew@pc01 ~ ]$ wget \
> https://github.com/atom/atom/releases/download/v1.35.0-beta0/atom-amd64.tar.gz
```

この拡張子のファイルは`gunzip`で解凍できます。

```bash
[ andrew@pc01 ~ ]$ gunzip atom-amd64.tar.gz && ls
atom-amd64.tar
```

`.tar.gz`ファイルを`gunzip`すると`.tar`ファイルになり、さらに`tar -xf`で通常のファイルシステムに戻すことができます。

```bash
[ andrew@pc01 ~ ]$ tar -xf atom-amd64.tar && mv \
atom-beta-1.35.0-beta0-amd64 atom && ls
atom atom-amd64.tar
```

逆に圧縮するには、`-c`でディレクトリから`.tar`にまとめ、`-z`で圧縮します。

```bash
[ andrew@pc01 ~ ]$ tar -zcf compressed.tar.gz atom && ls
atom  atom-amd64.tar  compressed.tar.gz
```

`gzip`でもファイルを圧縮することができます。

```bash
[ andrew@pc01 ~ ]$ gzip atom-amd64.tar && ls
atom  atom-amd64.tar.gz compressed.tar.gz
```

### Redirecting Input and Output

#### | / > / < / echo / printf

デフォルトでは、シェルコマンドは標準入力ストリーム(stdin、0)から入力を読み取り、標準出力ストリーム(stdout、1)に出力を書き込みます。
エラーが発生すると、それは標準エラーストリーム(stderr、2)に出力されます。

`echo`はデフォルトではstdoutに対してテキストを書き込みます。
ほとんどの環境では、それは単にターミナルに表示される、という意味です。

```bash
[ andrew@pc01 ~ ]$ echo "hello"
hello
```

パイプオペレータ`|`は、前のコマンドの出力を、後ろのコマンドの入力にリダイレクトします。

```bash
# wcはファイルの行数、単語数、バイト数を返すコマンド
[ andrew@pc01 ~ ]$ echo "example document" | wc
      1       2      17
```

`>`によるリダイレクトは出力先をstdoutから別のものに変更します。

```bash
[ andrew@pc01 ~ ]$ echo "test" > file && head file
test
```

`printf`は`echo`の改良版で、フォーマットやエスケープシーケンスが書けます。

```bash
[ andrew@pc01 ~ ]$ printf "1\n3\n2"
1
3
2
```

`<`はstdinではなく、別のところから入力を取得します。

```bash
# sortは入力を行単位でソートするコマンド
[ andrew@pc01 ~ ]$ sort <(printf "1\n3\n2")
1
2
3
```

ファイルの中身をコマンドに送る推奨された方法は、UUOCではなく`<`を使うことです。
これを使った場合、データは自然に感じる左から右ではなく、右から左に流れることになるので注意してください。

```bash
[ andrew@pc01 ~ ]$ printf "1\n3\n2" > file && sort < file
1
2
3
```

#### 0 / 1 / 2 / tee

`0`、`1`、`2`は、上記のようにそれぞれ標準入力、標準出力、標準エラーストリームを表します。
各ストリームは前述のように`|`、`<`、`>`各演算子を使ってリダイレクトできますが、それとは別に直接数値で書くこともできます。
その場合、標準出力は`>&1`、標準エラーは`>&2`となります。

```bash
[ andrew@pc01 ~ ]$ cat test
echo "stdout" >&1
echo "stderr" >&2
```

デフォルトでは標準出力も標準エラーもターミナルに出力されます。

```bash
[ andrew@pc01 ~ ]$ ./test
stderr
stdout
```

標準入力を`/dev/null`に送ると、標準エラーだけがターミナルに表示されます。

```bash
[ andrew@pc01 ~ ]$ ./test 1>/dev/null
stderr
```

標準エラーを`/dev/null`に送ると、標準出力だけがターミナルに表示されます。

```bash
[ andrew@pc01 ~ ]$ ./test 2>/dev/null
stdout
```

全てを`/dev/null`に送る場合はこうです。

```bash
[ andrew@pc01 ~ ]$ ./test &>/dev/null
```

標準出力の出力先を変更するのではなく、複数の出力先に送りたい場合は、`tee`コマンドを使います。

```bash
[ andrew@pc01 ~ ]$ ls && echo "test" | tee file1 file2 file3 && ls
file0
test
file0  file1  file2  file3
```


## Advanced

### Superuser

#### sudo / su

自分の名前を知りたいときは`whoami`です。

```bash
[ andrew@pc01 abc ]$ whoami
andrew
```

コマンドを別のユーザとして実行したい場合は、`sudo -u username`というコマンドで実行できます。
ただしそのユーザのパスワードが必要です。

```bash
[ andrew@pc01 abc ]$ sudo -u test touch def && ls -l
total 0
-rw-r--r-- 1 test test 0 Jan 11 20:05 def
```

`-u`を省いた場合のデフォルトはスーパーユーザ、通常は`root`であり、このユーザはあらゆる操作が無制限に可能です。

```bash
[ andrew@pc01 abc ]$ sudo touch ghi && ls -l
total 0
-rw-r--r-- 1 test test 0 Jan 11 20:05 def
-rw-r--r-- 1 root root 0 Jan 11 20:14 ghi
```

`su`で一時的に別のユーザになれます。
戻るときは`exit`です。

```bash
[ andrew@pc01 abc ]$ su test
Password:
test@pc01:/home/andrew/abc$ whoami
test
test@pc01:/home/andrew/abc$ exit
exit

[ andrew@pc01 abc ]$ whoami
andrew
```

`sudo`と`su`の違いについてもっと知りたい人は、[こちらの記事](http://bit.ly/2SKQH77)を読みましょう。

#### !!

スーパーユーザはソフトウェアのインストール、ユーザの作成などを行うことができる唯一のユーザです。
時々それを忘れてエラーを起こすことがあるでしょう。

```bash
[ andrew@pc01 ~ ]$ apt install ruby
E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?
```

頭に`sudo`をつけてコマンドを再入力するとうまくいきます。

```bash
[ andrew@pc01 ~ ]$ sudo apt install ruby
Reading package lists...
```

`!!`で同じことができます。
これは前回入力したコマンドを保持しているショートカットです。

```bash
[ andrew@pc01 ~ ]$ apt install ruby
E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?

[ andrew@pc01 ~ ]$ sudo !!
sudo apt install ruby
Reading package lists...
```

デフォルトでは、いちど`sudo`でコマンド実行に成功すると、その後15分間はパスワード入力なしに`sudo`コマンドを実行可能です。
15分を超えると、ふたたびパスワードを要求されるようになります。

### File Permissions

#### File Permissions

ファイルに対して読み込み(r)、書き込み(w)、実行(x)の操作をそれぞれ可能だったりできなかったりします。
ファイルへのアクセス権は`ls -l`コマンドで確認することができ、[10文字で表されます](https://qiita.com/rana_kualu/items/e2d4fbc75cc3271a188e)。

```bash
[ andrew@pc01 ~ ]$ ls -lh
total 8
drwxr-xr-x 4 andrew andrew 4.0K Jan  4 19:37 tast
-rwxr-xr-x 1 andrew andrew   40 Jan 11 16:16 test
-rw-r--r-- 1 andrew andrew    0 Jan 11 16:34 tist
```

各行の最初の文字は、ファイルの種類を表します。
d=ディレクトリ、l=シンボリックリンク、-=通常ファイルなど。
次の3文字はファイルの所有者(u)が持つパーミッション、次の3文字がファイル所有グループ(g)が持つパーミッション、最後の3文字が無関係なユーザ(o)の持つパーミッションです。

`r`はそのファイルを読み取り可能、`w`は書き込み可能、`x`は実行可能であるというパーミッションを表します。
ディレクトリが実行可能であるとは、そのディレクトリの内容を一覧表示できるという意味です。
この3権限のセットを1桁の数値で表すことがよくあります。
`x`が有効なら1、`w`が有効なら2、`r`が有効なら4というふうに2進数として計算します。
例として`r-x`であれば`r`と`x`が有効なので5です。
従って、上記例の3ファイルは順に755、755、644のパーミッションを持つというふうに表されます。

パーミッションの次にある数値は、そのファイルを参照しているリンクの数です。

その次の2つの文字は、それぞれ所有者名と、所有者のグループ名を表します。
今回は所有者もグループも全てandrewです。

さらにその次がファイルのサイズ、更新日時、そして最後にファイル名が表示されます。

`ls`の`-h`オプションは人間が読み取りやすいように出力を調整します。
すなわち`4096`などではなく`4.0K`と表示します。

#### chmod / chown

`chmod`で数値を与えることにより、ファイルのパーミッションを変更できます。

```bash
[ andrew@pc01 ~ ]$ chmod 777 test && chmod 000 tist && ls -lh
total 8.0K
drwxr-xr-x 4 andrew andrew 4.0K Jan  4 19:37 tast
-rwxrwxrwx 1 andrew andrew   40 Jan 11 16:16 test
---------- 1 andrew andrew    0 Jan 11 16:34 tist
```

あるいはプラスマイナスと`rwx`で直感的操作もできます。

```bash
[ andrew@pc01 ~ ]$ chmod +rwx tist && chmod -w test && ls -lh
chmod: test: new permissions are r-xrwxrwx, not r-xr-xr-x
total 8.0K
drwxr-xr-x 4 andrew andrew 4.0K Jan  4 19:37 tast
-r-xrwxrwx 1 andrew andrew   40 Jan 11 16:16 test
-rwxr-xr-x 1 andrew andrew    0 Jan 11 16:34 tist
```

ファイルの所有者は`chown`で変更できます。

```bash
[ andrew@pc01 ~ ]$ sudo chown marina test
```

グループの変更は`chgrp`です。

```bash
[ andrew@pc01 ~ ]$ sudo chgrp hadoop tist && ls -lh
total 8.0K
drwxr-xr-x 4 andrew andrew 4.0K Jan  4 19:37 tast
-----w--w- 1 marina andrew   40 Jan 11 16:16 test
-rwxr-xr-x 1 andrew hadoop    0 Jan 11 16:34 tist
```

### User and Group Management

#### Users

現在ログインしてる全ユーザの一覧が`users`で表示できます。
複数のSSHセッションを張ったりなどで同一ユーザが同時に何度もログインすることができます。

```bash
[ andrew@pc01 ~ ]$ users
andrew colin colin colin colin colin krishna krishna
```

ログインしていないユーザも含め全ユーザを確認するには、`/etc/passwd`を参照します。
なお、このファイルを変更するとユーザアカウントが破損し、ログインできなくなる可能性があるため、絶対に直接編集しないでください。

```bash
[ andrew@pc01 ~ ]$ alias au="cut -d: -f1 /etc/passwd \
> | sort | uniq" && au
 _apt
anaid
andrew...
```

ユーザの追加は`useradd`です。

```bash
[ andrew@pc01 ~ ]$ sudo useradd aardvark && au
_apt
aardvark
anaid...
```

ユーザの削除は`userdel`です。

```bash
[ andrew@pc01 ~ ]$ sudo userdel aardvark && au
_apt
anaid
andrew...
```

ユーザアカウント操作の詳細については[こちらを参照してください](http://bit.ly/2D4upIg)。

#### Groups

`groups`は、ユーザが属している全てのグループを表示します。

```bash
[ andrew@pc01 ~ ]$ groups
andrew adm cdrom sudo dip plugdev lpadmin sambashare hadoop
```

システム上の全てのグループを確認するには、`/etc/group`を参照します。
このファイルを理解していないかぎり、直接変更はしないでください。

```bash
[ andrew@pc01 ~ ]$ alias ag=“cut -d: -f1 /etc/group \
> | sort” && ag
adm
anaid
andrew...
```

グループの追加は`groupadd`です。

```bash
[ andrew@pc01 ~ ]$ sudo groupadd aardvark && ag
aardvark
adm
anaid...
```

グループの削除は`groupdel`です。

```bash
[ andrew@pc01 ~ ]$ sudo groupdel aardvark && ag
adm
anaid
andrew...
```

グループ操作の詳細については[こちらを参照してください](https://linux.die.net/man/8/groupmod)。

### Text Processing

#### uniq / sort / diff / cmp

`uniq`コマンドは、連続した重複行を削除して表示します。

```bash
[ andrew@pc01 man ]$ printf "1\n2\n2" > a && \> printf "1\n3\n2" > b

[ andrew@pc01 man ]$ uniq a
1
2
```

`sort`は行をアルファベット順、もしくは数値順に並び替えます。

```bash
[ andrew@pc01 man ]$ sort b
1
2
3
```

`diff`は、2つのファイルでどの行が異なるかを表示します。

```bash
[ andrew@pc01 man ]$ diff a b
2c2
< 2
---
> 3
```

`cmp`は、2つのファイルで最初に異なる場所のバイト数を表示します。

```bash
[ andrew@pc01 man ]$ cmp a b
a b differ: char 3, line 2
```

#### cut / sed

`cut`は、文字列をなんらかの区切り文字で分割するコマンドで、CSV処理などに向いています。
`-d`で区切り文字を指定し、`-f`は出力するインデックス(1始まり)を指定します。

```bash
[ andrew@pc01 man ]$ printf "137.99.234.23" > c

[ andrew@pc01 man ]$ cut -d'.' c -f1
137
```

`sed`は、ファイル内の文字列を別の文字列に置換するためによく使われます。

```bash
[ andrew@pc01 man ]$ echo "old" | sed s/old/new/
new
```

実のところ`sed`は非常に強力なユーティリティであり、この狭いスペースでは到底解説することができません。
`sed`はチューリング完全であり、他のプログラミング言語でできることなら何でもできます。
`sed`は、正規表現に基づいてテキストを検索・置換し、特定パターンに一致する、あるいは含む行を抽出したり、テキストファイルを非対話式に[In-place](https://ja.wikipedia.org/wiki/In-place%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0)編集したり、その他様々なことが行えます。
詳しくは以下のようなチュートリアルを参照してください。
・[https://www.tutorialspoint.com/sed/](https://www.tutorialspoint.com/sed/)
・[http://www.grymoire.com/Unix/Sed.html](http://www.grymoire.com/Unix/Sed.html)
・[https://www.computerhope.com/unix/used.htm](https://www.computerhope.com/unix/used.htm)


### Pattern Matching

#### grep

`grep`の名前は`search Globally for a Regular Expression and Print it`に由来します。
ファイルから、特定のパターンに一致するテキストを見つけるために使用されます。

```bash
[ andrew@pc01 ~ ]$ grep -e ".*fi.*" /etc/profile
# /etc/profile: system-wide .profile file for the Bourne shell (sh(1))
    # The file bash.bashrc already sets the default PS1.
    fi
    fi
...
```

普通に単語検索もできます。

```bash
[ andrew@pc01 ~ ]$ grep "andrew" /etc/passwd
andrew:x:1000:1000:andrew,,,:/home/andrew:/bin/bash
```

ファイル内の一致する行を見つけた後、他のプログラムでその行を処理させたい場合などに、`grep`は最適な選択です。

`grep`では、正規表現`-E`、複数文字列のいずれかとマッチさせる`-F`、ディレクトリ内のファイルを再帰的に検索する`-r`などのオプションが存在します。
これらのオプションは元々個別コマンド`egrep`、`fgrep`、`rgrep`として実装されていましたが、個別コマンドは非推奨になりました。

> おまけ
> いろいろな[コマンド名の由来](https://kb.iu.edu/d/abnd)を見てみよう。

#### awk

`awk`は、CSVなどのデリミタで区切られたデータファイルを読み取り、操作することを念頭に作られた言語です。
経験的に、`grep`はファイルから特定の文字列やパターンを見つけるのに適していて、`sed`はファイル内の文字列を1対1で置換するのに適しています。
そして`awk`は、ファイルからパターンを抽出して分析するのに適しています。

`awk`ができることの例として、2列のデータを含むファイルを処理してみます。

```bash
[ andrew@pc01 ~ ]$ printf "A 10\nB 20\nC 60" > file
```

行ごとにループし、数値を合計し、平均を出力するコードは以下のようになります。

```bash
[ andrew@pc01 ~ ]$ awk 'BEGIN {sum=0; count=0; OFS=" "} {sum+=$2; count++} END {print "Average:", sum/count}' file
Average: 30
```

`sed`と`awk`はどちらもチューリング完全な言語です。
これらはパターンマッチングとテキスト処理に非常に役立つ言語で、そしてそれぞれについて書かれた本もたくさんあります。
それらについて書き記すにはここには余白が足りません。
もっと調べてみてください。

> おまけ
> [`sed`と`grep`と`awk`の違いについて学ぼう](http://bit.ly/2AI3IaN)。


### Copying Files Over ssh

#### ssh / scp

`ssh`は、Unixベースのマシン同士がネットワークを介して接続する方法です。

```bash
[ andrew@pc01 ~ ]$ ssh –p <port> andrew@137.xxx.xxx.89
Last login: Fri Jan 11 12:30:52 2019 from 137.xxx.xxx.199
```

別のマシンに入ったのでプロンプトが変わりました。

```bash
[ andrew@pc02 ~ ]$ exit
logout
Connection to 137.xxx.xxx.89 closed.
```

マシン1でファイルを作成します。

```bash
[ andrew@pc01 ~ ]$ echo "hello" > hello
```

これをマシン2にコピーするには`scp`コマンドを使用します。
`ssh`はポート番号を`-p`で指定するのに対し、`scp`は`-P`で指定するのに注意しましょう。

```bash
[ andrew@pc01 ~ ]$ scp –P <port> hello andrew@137.xxx.xxx.89:~
hello                                         100%    0     0.0KB/s   00:00
```

確認のため、`ssh`でマシン2に入りましょう。

```bash
[ andrew@pc02 ~ ]$ ssh –p <port> andrew@137.xxx.xxx.89
Last login: Fri Jan 11 22:47:37 2019 from 137.xxx.xxx.79
```

そこにファイルが存在しているはずです。

```bash
[ andrew@pc02 ~ ]$ ls
hello  multi  xargs

[ andrew@pc02 ~ ]$ cat hello
hello
```

#### rsync

`rsync`は、ファイル間の差分を調べることにより、コピーされるデータ量を少なく抑えるファイルコピーツールです。

2つのディレクトリ`d`と`s`が存在し、`d`にはファイルがひとつ、`s`にはふたつ存在するとします。

```bash
[ andrew@pc01 d ]$ ls && ls ../s
f0
f0  f1
```

ディレクトリを`rsync`で同期すると、不足しているファイルだけがコピーされます。

```bash
[ andrew@pc01 d ]$ rsync -av ../s/* .
sending incremental file list...
```

最終的に`d`ディレクトリには、`s`ディレクトリに存在する全てのファイルが集まります。

```bash
[ andrew@pc01 d ]$ ls
f0  f1
```

`rsync`は`ssh`経由でも実行できます。

```bash
[ andrew@pc02 r ]$ ls

[ andrew@pc02 r ]$ rsync -avz -e "ssh -p <port>" andrew@137.xxx.xxx.79:~/s/* .
receiving incremental file list
f0
f1

sent 62 bytes  received 150 bytes  141.33 bytes/sec
total size is 0  speedup is 0.00
```

### Long-Running Processes

#### yes / nohup / ps / kill

ネットワークやハードウェアの問題によって、SSH接続が切断されることがよくあります。
その際、そのSSH接続から起動されていたプロセスには全てHUPシグナルが送られ、プロセスは強制終了されます。

`yes`コマンドは、停止されるまで"y"を出力し続けます。
これを`nohup`と一緒に実行しましょう。

```bash
[ andrew@pc01 ~ ]$ nohup yes &
[1] 13173
```

`ps`は、現在のユーザが実行しているプロセスを一覧表示します。
以下の例では、`yes`コマンドのPIDは13713です。

```bash
[ andrew@pc01 ~ ]$ ps | sed -n '/yes/p'
13173 pts/10   00:00:12 yes
```

ログアウトして再度ログインしてみましょう。

```bash
[ andrew@pc01 ~ ]$ ps | sed -n '/yes/p'
```

表示されなくなりました。
が、実際は裏で実行され続けていて`top`や`htop`には出てきます。

```bash
[ andrew@pc01 ~ ]$ top -bn 1 | sed -n '/yes/p'
13173 andrew    20   0    4372    704    636 D  25.0  0.0   0:35.99 yes
```

`kill`コマンドに`-9`オプションを付けて、このプロセスを強制終了しましょう。

```bash
[ andrew@pc01 ~ ]$ kill -9 13173
```

これでプロセスが終了したため、もう`top`にも出てきません。

```bash
[ andrew@pc01 ~ ]$ top -bn 1 | sed -n '/yes/p'
```

#### cron / crontab / >>

cronはタスクを定期的に実行する簡単な方法を提供します。

`crontab –e`コマンドでテキストエディタが開き、cronを編集することができます。
以下の行を追加しましょう。

```bash
* * * * * date >> ~/datefile.txt
```

毎分`date`コマンドを実行し、出力をファイルに保存します。

```bash
[ andrew@pc02 ~ ]$ head ~/datefile.txt
Sat Jan 12 14:37:01 GMT 2019
Sat Jan 12 14:38:01 GMT 2019
Sat Jan 12 14:39:01 GMT 2019...
```

再び`crontab`を編集し、該当行を削除すると、このジョブは停止します。
ジョブの頭にある`* * * * *`は順に分(0-59)、時間(0-23)、日(1-31)、月(1-12)、曜日(0-6もしくはSun-Sat)を表します。
各`*`を数値に置き換えることで、特定の日や特定の時間にのみジョブを実行することができるようになります。

曜日に関係なくジョブを実行したい場合は、5番目の`*`を`*`のままにしておきます。
全ての項目を`*`にすると毎分実行されるようになり、これが利用可能な最小間隔になります。
システムが再起動されたときだけ実行するように設定することもでき、これは`* * * * *`のかわりに`@reboot`と記載します。
また、1時間あるいは一日のうち特定の回数だけ実行するようにしたり、月や曜日や日など複数項目の組み合わせで指定することも可能です。

もっと詳しく知るには[こちらを参照してください](http://bit.ly/2FxIr75)。

### Miscellaneous

#### pushd / popd

ディレクトリの移動をスタックするには、`cd`ではなく`pushd`と`popd`を使います。
以下の例ではホームディレクトリから開始します。

```bash
[ andrew@pc01 ~ ]$ pwd
/home/andrew
```

フルパスの`pushd`でディレクトリを移動します。

```bash
[ andrew@pc01 ~ ]$ pushd /etc/java/security/security.d/
/etc/java/security/security.d ~
```

`pushd`でサブディレクトリに移動します。

```bash
[ andrew@pc01 security.d ]$ pushd ~/test/
~/test /etc/java/security/security.d ~
```

`pushd`によって積まれたスタックは、表示の一番左側に追加されます。
`popd`すると一番最後に追加されたディレクトリに戻ります。

```bash
[ andrew@pc01 test ]$ popd
/etc/java/security/security.d ~

[ andrew@pc01 security.d ]$ pwd
/etc/java/security/security.d
```

何度も`popd`すると、最終的に`pushd`を始めたディレクトリに戻ります。

```bash
[ andrew@pc01 security.d ]$ popd
~
```

#### xdg-open

`xdg-open`は、ファイルをデフォルトのアプリケーションで開くコマンドです。
HTMLファイルをブラウザで開きたい場合などに便利です。
MacOSで言う`open`コマンドに相当します。

```bash
[ andrew@pc01 security.d ]$ xdg-open index.html
```

![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26088/cb352371-b7f0-1a4e-7a66-c184f21a5d9a.png)

#### xargs

`xargs`引数のリストに対して、ループ的にコマンドを実行します。

以下はカレントディレクトリ、親ディレクトリ、親の親ディレクトリに対して`ls`を実行する例です。

```bash
[ andrew@pc01 ~ ]$ export lv=".\n..\n../.."

[ andrew@pc01 ~ ]$ printf $lv | xargs ls
.:
multi  file

..:
anaid  andrew  colin...

../..:
bin    dev   index...
```

引数は、`–I`オプションを使うことで次のコマンドの引数に割り当てることができます。

以下はカレントディレクトリ、親ディレクトリ、親の親ディレクトリに対して`cd`と`pwd`を実行する例です。

```bash
[ andrew@pc01 ~ ]$ printf $lv | xargs -I % sh -c 'cd %; pwd %'
/home/andrew
/home
/
```

もっと詳しく知るには、[この素晴らしいチュートリアル](https://shapeshed.com/unix-xargs/)をご覧ください。


## Bonus: Fun-But-Mostly-Useless Things

楽しいけど役に立たないもの。

#### w / write / wall / lynx

`w`は誰がログインしていてさらに何をしているかを表示する、`who`の詳細版です。

```bash
[ andrew@pc01 ~ ]$ w
 17:32:42 up 434 days,  3:11,  8 users,  load average: 2.32, 2.46, 2.57
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
colin    pts/9    137.xx.xx.210    03Jan19  5:28m  1:12   0.00s sshd: colin [priv]
andrew   pts/10   137.xx.xx.199    11:05    1.00s  0.15s  0.04s sshd: andrew [priv]
colin    pts/12   137.xx.xx.210    03Jan19 34:32   1.59s  1.59s –bash
...
```

別のユーザに対して`write`でメッセージを送信することができます。

```bash
[ andrew@pc01 ~ ]$ echo "hello" | write andrew pts/10

Message from andrew@pc01 on pts/10 at 17:34 ...
hello
EOF
```

`wall`は`write`と似ていますが、ログイン中の全ユーザに同じメッセージを送信します。
メールやTwitterやWhatsAppといったアプリが普及する前は、メッセージを送るために`write`や`wall`がよく使われていました。

`lynx`はテキストベースの、完全に機能するWebブラウザです。

![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26088/0ee2ab51-fb9c-25aa-3cea-e9911ca3664f.png)

#### nautilus / date / cal / bc

`nautilus`は新しいセッションを開き、GUIのファイルマネージャをオープンします。

`date`は現在の日付と時刻を表示します。

```bash
[ andrew@pc01 ~ ]$ date
Fri Jan 11 17:40:30 GMT 2019
```

`cal`は今月のカレンダーをテキストで表示します。
本日の日付は強調表示されます。

```bash
[ andrew@pc01 ~ ]$ cal
    January 2019
Su Mo Tu We Th Fr Sa
       1  2  3  4  5
 6  7  8  9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
```

`bc`は簡易的な電卓です。
かわりにPythonを使いましょう。

```bash
[ andrew@pc01 ~ ]$ bc
bc 1.06.95 ...
20/4
5
```


これでおしまいです！
このリストに追加する必要のある機能や、クールなコマンドを知っていたら教えてください。
またタイプミスや間違いがあったときも教えてください。
ベストを尽くしたつもりですが、きっとたくさんあると思います。

この投稿が参考になったのであれば、ぜひ[コーヒーを買ってください](https://ko-fi.com/Z8Z811E9B)。

## Update 8 July 2019

2年前に書かれた、[とてもよく似た記事を発見しました](https://bookmarks.ecyseo.net/?EAWvDw)。
フランス語がわかるのであれば、私の記事の補完に最適だと思います。


# コメント欄

「素晴らしいポスト！Andrewサンクス！」
「同じ投稿をPowerShellでやってくれたら思い残すことはない。」
「`screen`。新しいSSH接続を開く。そのセッションはログオフしてもプログラムはずっと実行されたままで、さらに再接続して続きもできる。」「`tmux`はいいぞ。`screen`と似てるけどもっと便利。」
「`disown`。既に動かした後のプログラムを、後からバックグラウンドに持っていける。」
「`ipython`と`trash-cli`を是非。」
「`date +%s`でUNIXタイムを表示できるよ。」
「`ncdu`初めて知った。これ便利！」
「MacとFreeBSDでは`top`のユーザ名オプションは`-U`だったよ。」
「`kill -9`はいかん、`kill -15`か単に`kill`ってするんだ。`-15`はOSに通知が行く、`-9`はいきなり電源押して切るようなものだ。」
「`source`と`sh`は同じ結果になることもあるけど全く同じではない。`source`は現在の`bash`上で実行されて`export`した変数も使える。`sh`は新たなシェルを起動するし`export`した変数は使えない。」
「`bash`コマンドって言うけど実際はbashコマンドではなくてUnix/Posixコマンドが多いぞ。」
「`xdg-open`の画像がWindowsっぽいんだけどどゆこと？」「[MobaXTerm](https://mobaxterm.mobatek.net/)を使ってWindowsからUbuntuにログインしてるからなのだ。」
「コマンドプロンプトに`|`を使っててパイプとわかりにくいのでプロンプトは`$`にしてほしい。」「ありがとう更新したよ！」
「聖なる牛！」


# 感想

チュートリアル形式でわかりやすい、bashコマンド入門記事です。
最後まで読めばbashスクリプトをバリバリ組めるようなエキスパートになれるかというと怪しいですが、汎用的なbashコマンドについてはひととおり使えるようになっていることでしょう。

LPICのLevel3まで持ってたらよゆーよゆーとか思ってたけど、わりと知らないコマンドも多かった。
知っていると知らないとでは大違いなので、コマンドや機能が存在していることそのものを認識しておくと、今後役に立つことでしょう。
いきなり詳しく知る必要はありません。
詳細は必要になったときに`man`なりググるなりすればいいんですよ。

あと原文は、こうすればこうなりますよというのは書かれているんだけど、"何故"そうなるかが書かれていないところがあってちょっと躓く感じがありました。
和訳にあたりそのあたりを一部補完しています。

トップコメの[Holy cow!](https://dev.to/awwsmm/101-bash-commands-and-tips-for-beginners-to-experts-30je#comment-node-144159)はどう捉えたらいいのかよくわからなかった。