えいあーるれいの技術日記

ROS2やM5 Stack、Ubuntuについて書いています

記事上で煽ってたらアクロバティックなC++ (もはやbash) を提出された話

昨日の記事の続きです。↓

ar-ray.hatenablog.com


簡単に説明すると、コマンドラインからも実行可能&コンパイル可能なプログラムを公開したら、「shebangつけろ」というクソコメントが飛んできたのでプログラムの解説&煽る記事です。


すると、これを読んだ(つよつよ)学生プログラマがまさかのアクロバティックなスクリプトを提出してきました!


なんと、shebangが埋め込まれている!!!


プログラム

#!/bin/bash
#if NULL

THISFILE=`basename $0`
NAME=`basename $0 .cpp`

FILE=$(echo "#if NULL"; echo "#!/bin/bash"; tail -n +3 $THISFILE)
printf '%s\n' "$FILE" > $THISFILE
g++ -o $NAME $THISFILE
FILE=$(echo "#!/bin/bash"; echo "#if NULL"; tail -n +3 $THISFILE)
printf '%s\n' "$FILE" > $THISFILE
./$NAME
rm $NAME

exit
#endif

#include <iostream>

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

このスクリプトコンパイル不可能です。しかし、実行権限を付与した上で実行すると、bashで実行されます。

簡単な動作手順を示します。

  1. #!/bin/bash : bashで実行されます。
  2. #if NULL : 実はあまり意味がないが、C++からのコンパイル除外。bashではコメント扱い。
  3. THISFILE='basename $0' : ファイル名を取得
  4. NAME='basename $0 .cpp' : .cppを除いたファイル名を取得
  5. FILE=$(echo "#if NULL"; echo "#!/bin/bash"; tail -n +3 $THISFILE) : トリック(後述)
  6. printf '%s\n' "$FILE" > $THISFILE 先ほどのトリックの結果を用いてファイルを上書き
  7. g++ -o $NAME $THISFILE : コンパイル
  8. FILE=$(echo "#!/bin/bash"; echo "#if NULL"; tail -n +3 $THISFILE) : 5のトリックの逆
  9. printf '%s\n' "$FILE" > $THISFILE : 6の逆
  10. ./$NAME : 7のバイナリの実行
  11. rm $NAME : ファイルの削除
  12. exit : スクリプトの終了


次に5と8のトリックについて説明します。


トリック:シェル的文字列の入れ替え

5では、C++プログラムのコンパイルが通るように#if NULL#!/bin/bash を入れ替えます。


まず、$(echo "#if NULL"; echo "#!/bin/bash"; までの文で2行目を入力します。

次に、tail -n +3 $THISFILEで該当ファイルの3行目から末尾までを出力します。(=2行目までを除去)

そして、6行目の printf でコード整形してファイルに出力します。(これが無いと\nが出力されません)

コンパイルが終わったら、8, 9のトリックで元に戻します。


これを動画にするとこうなります。


まさにアクロバティック!!!

もはやC++じゃなくてBashファイルですが…


煽っていましたが…

煽りに対するこの回答は本質を突いているかどうかは怪しいところですが、こんな華麗かつ無駄なプログラムを提示されると馬鹿馬鹿しさを超えて美しさすら感じてしまいます。


アウトプットにツッコミを入れる際は流石にこのレベルほどではなくとも「おお!?」と思わせるアウトプットで返していきたいですね。


補足

tail -n +3 $THISFILE-n は不要みたいです。