Недавно пришлось мне опять/снова погрузиться в чудесный мир программирования Linux скриптов. В принципе, дело не очень хитрое, но поскольку мне попадаются такие задачи не часто, то каждый раз изучаю заново. Знаю точно, что завтра многое забуду и через месяц опять буду гуглить, как сделать то или это. Проблема еще оказывается в том, что зачастую не пишешь скрипт заново, а модифицируешь существующий, уже написанный кем-то. А он может быть не bash, а sh или еще что-то… Различия в синтаксисе есть, что работает в sh по идее должно работать и в bash, но не всегда наоборот. А если там dash или ash? Я не знаю… Различия в этих скриптовых языках все же есть, и они сбивают с толка. Ну и конечно, лично для меня, вишенка на торте, когда скрипт вызывает какой нибудь sed или awk и там такие параметры в командной строке, что смотришь на них и диву даешься. Понятно, что это все зависит от квалификации программиста, но вот у меня не все в голове помещается. И вот сейчас мое терпение лопнуло и я подумал, что отныне хочу попробовать писать скрипты на c++…

Я понимаю, что для true системного администратора моя мысль может показаться крамольной. Но почему бы и нет?

Итак, мысль очень простая. Я хочу писать c++ скрипты так же, как пишутся обычные скрипты, то есть первая строка скрипта должна содержать shebang и указание на путь к «интерпретатору»:

#!/bin/c++

Последующие строки скрипта будут просто обычной программой на c++.

Я должен подготовить «интерпретатор» скрипта c++. Написать его можно на чем угодно, да хоть на bash (это в последний раз, хотя не точно). Конечно он будет не интерпретатором, а компилятором.

Вот что у меня получилось:

#!/bin/bash

msg_file=/dev/null
#msg_file=/dev/stdout

tmp_path=$HOME"/.cache/c++/"
mkdir -p $tmp_path
tmp_file=$1".c++"
exe_file=$1".bin"
if test $1 -nt  $tmp_path$exe_file; then
    echo "Need to recompile.." > $msg_file
    tail -n +2 $1 > $tmp_path$tmp_file
    eval "g++ -o $tmp_path$exe_file $tmp_path$tmp_file > /dev/null 2>&1"
    if [ $? -eq 0 ]
    then
	echo "Compiled ok" > $msg_file
    else
	echo "Compile error" > $msg_file
	exit 255
    fi
fi
eval "$tmp_path$exe_file $@1"

Этот скрипт делает все, что нужно. В качестве временной папки я выбрал папку ~/.cache/c++. В эту папку будет копироваться исходный скрипт, но без первой строки с shebang. Делается это командой tail. Имя нового файла будет как у исходного скрипта, но с расширением c++. В той же самой папке будет собираться бинарник, с расширением .bin. Но, сперва, конечно, делается проверка «if test» на время создания бинарника. Компиляция происходит только если существующий бинарник устарел по времени по отношению к исходному «скрипту». Запускается бинарник командой eval и ему передаются все исходные параметры.

Этот Файл c++ нужно скопировать в папку /bin и сделать его исполняемым (chmod a+x).

Попробую написать свой первый «c++ скрипт»:

#!/bin/c++

#include <stdio.h>
#include <iostream>

using namespace std;

int main( int argc, char *argv[] )
{
    cout << "hello world!\n";
    for( int i=0; i<argc; i++)
    {
	cout << "Param" << i <<  " is " << argv[i] << "\n";
    }
    return 60+argc;
}

Эта программа просто печатает список входных параметров и возвращает их количество + 60.
Запускаю мой «скрипт»:



Работает!!!

Если сделать в c++ коде ошибку, то программа не запустится, так как не скомпилируется, но echo $? вернет 255. Но так и задумано было.

Использование c++ дает громадные возможности. Во-первых, привычный синтаксис. Во-вторых, стандартные классы вроде std::vector, std::map или std::string и другие — незаменимые вещи. Та же строка — что хочешь с ней делай, ищи в строке, разбивай на подстроки, разъединяй и властвуй, получай массивы. И не нужны мне ни sed ни awk. В-третьих, отладчик — господи! какое счастье! у меня для скрипта есть отладчик gdb! Дальше, можно использовать std::filesystem (если компилятор позволяет). Можно продолжать…

К сожалению, у меня часто бывает, что сперва сделаю, а потом подумаю: «а вдруг кто-то такое уже сделал?». И в самом деле, я оказывается далеко не первый, кто придумал делать так же. Вот пример: https://github.com/dimgel/cpp-linux-scripts идея та же, реализация другая. Потом оказалось, что есть и другие реализации.

В общем, испытал небольшое разочарование в собственной неисключительности. Однако, поискал по Хабру — похожего не нашел. Может кому-то покажется хотя бы любопытным?