コンパイルして実行するシェルスクリプト C,C++

# この記事について
この記事ではC,C++のプログラムファイルをコンパイルして実行するシェルスクリプトを作成してプログラミングの学習を効率的にできるようになることを目的としています。

以下はこの記事で作成するコマンド"go"でhello.cppをコンパイル・実行しています。

```bash
$ go hello
Hello, World!
```
※2019/2/5 追記：この記事はプログラミング言語Goとは一切関係がありません。誤解を招くネーミングをしてしまい申し訳ございません。

<b>対象読者</b>
・C,C++を学習している人
・コンパイルして実行する作業を効率的にしたい人
・シェルスクリプトに興味があるけどあまり知らない人
<b>記事の更新</b>
この記事は私のプログラミング学習に応じて編集していきたいと考えております。本記事について不備・アドバイス等がありましたら遠慮なくお願いします。

## 動作環境の確認

```bash
$ cat /etc/redhat-release 
CentOS Linux release 7.3.1611 (Core) 

$ bash --version | grep bash
GNU bash, バージョン 4.2.46(1)-release (x86_64-redhat-linux-gnu)

$ g++ --version | grep g++
g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-11)
```
動作環境の確認はこんな感じでいいのですかね？？？

# C++ (or C) のコンパイルと実行
## 通常のコンパイルと実行
以下のようなC++のファイルをコンパイルして実行するときは以下のようになるかと思います。

```cpp:hello.cpp
#include <iostream>
using namespace std;

int main() {
    cout << "Hello, World!" << endl;
    return 0;
}
```

実行すると

```bash
$ g++ hello.cpp -o hello
$ ./hello
Hello, World!
```

Cでも似た感じですので以降はC++で考えることとします。

```bash
$ gcc hello.c -o hello
$ ./hello
Hello, World!
```

プログラミング言語の学習の時は「ちょこっとコードを書いてはコンパイルして実行」ということが頻繁にあるかと思います。
しかし、コマンドラインでいちいちコンパイルしてから実行するのは面倒です。

## 今回作成するシェルスクリプトでコンパイルと実行
今回はシェルスクリプトでコンパイルと実行をしてくれる"go"というコマンドを作成します。

```bash
$ go hello
Hello, World!
```

コマンドが一つで短いといいですね。

# コマンド(シェルスクリプト)を作ってみる

## 使うコマンドの確認

シェルスクリプトを作る前に以下のコマンドを試してみましょう。

```bash
$ g++ hello.cpp -o hello && ./hello
Hello, World!
```

一行にまとまりましたが、これを手打ちするのは面倒です。 

## 簡単なシェルスクリプト

簡単に書いてしまうとこんな感じです。

```bash:go1.sh
#!/bin/bash
g++ $1.cpp -o $1 && ./$1
```

これに実行権限をつけて同ディレクトリにあるhello.cppをコンパイルして実行してみましょう。

```bash
$ chmod +x go1.sh
$ ./go1.sh hello
Hello, World!
```

C言語のファイルをコンパイル・実行したい時はgo1.shの
g++, .cppをgcc, .cに変更すればいいですね！

<b>go1.shについて簡単に解説</b>
* １行目の"```#!/bin/bash```"はシェバン（Shebang）という部分です。このファイル（go1.sh）を実行するためのインタプリタを指定するために書く必要があります。<br>シェバンについてはこちらの記事がとても参考になると思います。<br>
[#!/bin/sh は ただのコメントじゃないよ！ Shebangだよ！](https://qiita.com/mohira/items/566ca75d704072bcb26f)
*  ２行目の"```$1```"はシェルスクリプトの第一引数を表しています。<br>また、"```&&```"は条件判断の時によく用いられますが、ここでは短絡評価を利用するために使っています。つまりコンパイル```g++ hello.cpp -o hello```に問題がなければ生成されたファイルを実行```./hello```することができるのです。逆に、コンパイルエラーが発生した時はファイルを実行```./hello```することはありません。


## シェルスクリプトを拡張（オプションを付ける）

go1.shでも十分役に立ちますが限界はすぐに来ます。g++にコンパイルオプション・リンクオプションを付けたくなって来ます。<br>さらに自分でオプションを定義すると便利です。例えばgo2.shではncursesによる端末制御やpthreadによるマルチスレッド、OpenGLの学習の時に簡単にオプションをつけることができます。

```bash:go2.sh
#!/bin/bash

cflags="-g -Wall"

while test "$1" != ""; do
  case $1 in
    -nc) cflags="$cflags -lncurses" ;;
    -pt) cflags="$cflags -pthread" ;;
    -gl) cflags="$cflags -L/usr/X11R6/lib -lglut -lGLU -lXmu -lGL -lX11 -lm" ;;
    -*) cflags="$cflags $1" ;;
    *) break ;;
  esac
  shift
done

filename=${1%.*}

shift
arguments="$@"

g++ $cflags $filename.cpp -o $filename && ./$filename $arguments
```

<b>go2.shについて簡単に解説</b>
* ３行目の"```cflags```"はg++のオプションが入る変数です。
* ５〜１４行目のwhile文ではシェルスクリプトの引数を解釈してそれに伴い処理していきます。引数が```-```から始まるものはみな"```cflags```"に追加されていきます。"```shift```"は引数を一つずらします。具体的には"```$2, $3```"が、"```$1, $2```"になります。
* １６行目の"```${1%.*}```"はパラメータ展開です。ここでは拡張子があったときにそれを取り除きます。例えばシェルスクリプトの第一引数がhello.cppだったときにも変数```filename```にhelloを代入します。つまり以下は同じ処理をしています。

```bash
$ ./go2.sh hello
$ ./go2.sh hello.cpp
```

パラメータ展開についてはこちらの記事がとても参考になると思います。
[シェルの変数展開](https://qiita.com/bsdhack/items/597eb7daee4a8b3276ba)

* １９行目の"```$@```"は引数全てを表します。しかし、すでに"```shift```"をされているため、以下のC++のファイルを実行すると残りの引数（show_args.cppの後の引数）が入ります。

```cpp:show_args.cpp
#include <iostream>
using namespace std;

int main(int argc, char **argv) {
    for(int i = 0; i < argc; i++) {
        cout << argv[i] << endl;
    }
    return 0;
}
```

実行すると

```bash
$ ./go2.sh show_arg.cpp "Who are you ?"
./show_args
who
are
you
?
```

## コマンドとして使えるようにする

それでは作成したシェルスクリプトをコマンドとして使えるようにしてみましょう。
go2.shをパスが通ったディレクトリに名前を"go"に変更してコピーします。（実行権限をつけるのを忘れずに）

```bash
$ chmod +x go2.sh
$ cp go2.sh ~/bin/go
```

これでどのディレクトリにいてもこのシェルスクリプトを使うことができます。

```bash
$ go hello
Hello, World!
```

※ 注意：コマンド名が"go"だとGo言語のツールと名前が衝突してしまうおそれがありますので別名にするなどの対応をしてください。


<b>パスを通す</b>
コマンドサーチパスを追加します。
パスが通ったディレクトリを探すには以下のコマンドが便利です。

```bash
$ echo $PATH | tr ":" "\n"
/usr/local/bin
/usr/bin
/bin
（以下省略）
```

出力されたディレクトリ下にgoを持って来ればコマンドとして使えます。
また、パスが通ったディレクトリを作成することもできます。例えばホームディレクトリ下に作成してみます。(~/bin)

```bash
$ mkdir ~/bin
$ echo 'export PATH="$PATH:$HOME/bin"' >> ~/.bash_profile
$ . ~/.bash_profile
```

２行目がパスを通す作業になります。テキストエディタで.bash_profileを開いて"```export PATH="$PATH:$HOME/bin```"を追記しても構いません。これでパスが通ったディレクトリを作成することができます。

パスを通すことについてはこちらの記事がとても参考になると思います。
[PATHを通すとは？ (Mac OS X)]
(https://qiita.com/soarflat/items/09be6ab9cd91d366bf71)
[PATHを通すために環境変数の設定を理解する (Mac OS X)](https://qiita.com/soarflat/items/d5015bec37f8a8254380)


<b>エイリアスを定義してコマンドとして使う</b>
パスが通っていないディレクトリにあってもエイリアスを定義することによってコマンドとして使えるようにすることもできます。
例えば```alias go='~/bin/go2.sh'```を.bashrcに追記することによってgoが使えるようになります。

## シェルスクリプト以外のアプローチ

コンパイルして実行するという操作はシェルスクリプト以外でも実現できます。

* .bashrc等にシェル関数を定義する
* Makefileを作成する
* Tiny C Compilerを使う

こちらの記事もとても参考になります。
[Cのコンパイルをちょっとだけ簡単にする](https://qiita.com/ine1127/items/752a6300cc1e053f2c7c)

# 余談
普段私はちょこっとコーディングするときにはvimを使っています。vimからこのgoをよびだせばvimから抜けずにコーディングを続けることもできます。
```:w | :!go %```


# 参考記事
[#!/bin/sh は ただのコメントじゃないよ！ Shebangだよ！](https://qiita.com/mohira/items/566ca75d704072bcb26f)
[シェルの変数展開](https://qiita.com/bsdhack/items/597eb7daee4a8b3276ba)
[PATHを通すとは？ (Mac OS X)]
(https://qiita.com/soarflat/items/09be6ab9cd91d366bf71)
[PATHを通すために環境変数の設定を理解する (Mac OS X)](https://qiita.com/soarflat/items/d5015bec37f8a8254380)
[Cのコンパイルをちょっとだけ簡単にする](https://qiita.com/ine1127/items/752a6300cc1e053f2c7c)