夕食を食べてる途中に思いついたC++でbash実行アルゴリズム。
「絶対コンパイルされない記述の中にインストール用スクリプト入れたら1つのファイルで完結するんじゃね??」
↑これを実装してTwitterに載せたところ、結構伸びました。
実行可能なC++ファイル…👀
— Ar-Ray (@Ray255Ar) 2022年9月29日
これ、ファイル1つでインストールできて局所的に役立つのではないか…? pic.twitter.com/q0Qq6d0bfI
一方で、「shebangつけたほうがいい」などの意味不明なコメントも見られ、非常に頭にきたので本来は記事化する予定はなかったですが、記事にします。
再現コード
私が実行に使用したコードはこちらになります。(example.cpp)
#if NULL THISFILE=`basename $0` NAME=`basename $0 .cpp` g++ -o $NAME $THISFILE ./$NAME rm $NAME exit #endif #include <stdio.h> int main() { printf("hello world\n"); }
このプログラムは非常にシンプルなHelloWorldのプログラムであり、普通は以下のように実行します。
g++ example.cpp -o example ./example
これをbashで解釈される性質を利用(悪用)して「実行可能な」C++ファイルを作成したわけです。
利用したのは#if NULL
(0でも可)によるコンパイラからの除外です。
#if ~ #endif
は条件付きコンパイルで、(マクロ含む)コンパイル不要な条件式の結果が真である場合にコンパイル対象に入れることができます。
そもそも定義されているかどうかによって分岐する #ifdef
#ifndef
などもあり、プラットフォームやOSなどに応じて使い分けます。
このプログラムはbashで実行した際に次のように遷移していきます。
#if NULL
: コメントとして無視されます(C++では#endif
まで無視されます。)THISFILE='basename $0'
: 自分のファイル名example.cpp
を取得します(バッククォートを'
に変えています)NAME=
basename $0 .cpp` : .cppを取り除きますg++ -o $NAME $THISFILE
: コンパイルします。-o
オプションで後ろに$NAME
(example
に置換)というバイナリを生成します。./$NAME
バイナリ./$NAME
を実行します。rm $NAME
バイナリ$NAME
を削除します。exit
bashファイルとしての処理が終了します。#endif
: 以降、C++ファイルが続きます。
この記法は #
を無視するスクリプト言語の仕様と #if ~ #end
によるコンパイル除外が可能なCの仕様を組み合わせてあたかもコンパイルなしで ./a.out
ができる マジック のような
「懐かしい」と「面白い」を狙った投稿でした。
しかし、一部のプログラマセンスを疑うようなコメントがきて非常に腹が立ったので、ここで煽ろうと思います。
どんな投稿なのかはぜひ、上の伸びたツイートの引用RTをご覧ください。
指摘するならここだぞポイント
なぜ 「shebang
を付けろ」がプログラマセンスを疑うような投稿であるかを以下に示します。
- C/C++の構造上、shebangが使えない
.cpp
の拡張子でbash実行ができるようなファイルは拡張子の意味がない。(←ネタであることが理解できていない、読解力不足)- どうしてもbashで実行したいなら
bash example.cpp
でいいじゃん。shebangよりも明示的じゃない。
1は 1行目に #if NULL
と競合するのでどう考えても無理です(C/C++側がコンパイルするため)。仮にできるなら教えてほしい。弟子にしてください。
2、3は言うまでもないでしょう。読みやすくするなら言語を混ぜていいわけがないですよね???
以上より、指摘するなら
「なにが実行可能なC++だよwwwww結局内部でbash動かしてコンパイルしてるだけじゃん」
です。shebangつけろコメントをした人は反省しなさい。
補足:shebangは2行目に書くと意味ない
macで実験しました。
macはzshのターミナルですが、少なくとも私の環境では自動的にbashで実行されるようです。
↓これなら大丈夫
本当に変に伸びたツイートには必ずと言っていいほどクソリプがつくので相手にするのが面倒です。
…自戒の念も込めて。