Подтвердить что ты не робот

Запустите файл C или С++ как script

Итак, это, вероятно, длинный снимок, но есть ли способ запустить файл C или С++ как script? Я пробовал:

#!/usr/bin/gcc main.c -o main; ./main

int main(){ return 0; }

Но он говорит:

./main.c:1:2: error: invalid preprocessing directive #!
4b9b3361

Ответ 1

Для C вы можете посмотреть tcc, Tiny C Compiler. Выполнение кода C как script является одним из возможных вариантов использования.

Ответ 2

Краткий ответ:

//usr/bin/clang "$0" && exec ./a.out "[email protected]"
int main(){
    return 0;
}

Хитрость в том, что ваш текстовый файл должен быть как действующим кодом C/C++, так и сценарием оболочки. Не забудьте exit из сценария оболочки до того, как интерпретатор достигнет кода C/C++, или вызовите магию exec.

Запустите с chmod +x main.c; ./main.c.

Подобный шебангу #!/usr/bin/tcc -run не нужен, потому что Unix-подобные системы уже будут выполнять текстовый файл внутри оболочки.

(адаптировано из этого комментария)


Я использовал его в своем C++ сценарии:

//usr/bin/clang++ -O3 -std=c++11 "$0" && ./a.out; exit
#include <iostream>
int main() {
    for (auto i: {1, 2, 3})
        std::cout << i << std::endl;
    return 0;
}

Если ваша строка компиляции слишком сильно увеличивается, вы можете использовать препроцессор (адаптированный из этого ответа), как показывает этот простой старый код C:

#if 0
    clang "$0" && ./a.out
    rm -f ./a.out
    exit
#endif
int main() {
    return 0;
}

Конечно, вы можете кэшировать исполняемый файл:

#if 0
    EXEC=${0%.*}
    test -x "$EXEC" || clang "$0" -o "$EXEC"
    exec "$EXEC"
#endif
int main() {
    return 0;
}

Теперь для действительно эксцентричного разработчика Java:

/*/../bin/true
    CLASS_NAME=$(basename "${0%.*}")
    CLASS_PATH="$(dirname "$0")"
    javac "$0" && java -cp "${CLASS_PATH}" ${CLASS_NAME}
    rm -f "${CLASS_PATH}/${CLASS_NAME}.class"
    exit
*/
class Main {
    public static void main(String[] args) {
        return;
    }
}

Программисты D просто помещают шебанг в начало текстового файла, не нарушая синтаксис:

#!/usr/bin/rdmd
void main(){}

Видеть:

Ответ 3

$ cat /usr/local/bin/runc
#!/bin/bash
sed -n '2,$p' "[email protected]" | gcc -o /tmp/a.out -x c++ - && /tmp/a.out
rm -f /tmp/a.out

$ cat main.c
#!/bin/bash /usr/local/bin/runc

#include <stdio.h>

int main() {
    printf("hello world!\n");
    return 0;
}

$ ./main.c
hello world!

Команда sed принимает файл .c и удаляет строку хеширования. 2,$p означает печать строк от 2 до конца файла; "[email protected]" расширяется до аргументов командной строки для runc script, т.е. "main.c".

Выход sed передается в gcc. Передача - в gcc сообщает ему читать из stdin, и когда вы это делаете, вам также нужно указать исходный язык с -x, поскольку у него нет имени файла для угадывания.

Ответ 4

Так как строка shebang будет передана компилятору, а # указывает директиву препроцессора, она задохнется на #!.

Что вы можете сделать, это вставить make файл в файл .c(как обсуждалось в этот поток xkcd)

#if 0
make [email protected] -f - <<EOF
all: foo
foo.o:
   cc -c -o foo.o -DFOO_C $0
bar.o:
   cc -c -o bar.o -DBAR_C $0
foo: foo.o bar.o
   cc -o foo foo.o bar.o
EOF
exit;
#endif

#ifdef FOO_C

#include <stdlib.h>
extern void bar();
int main(int argc, char* argv[]) {
    bar();
    return EXIT_SUCCESS;
}

#endif

#ifdef BAR_C
void bar() {
   puts("bar!");
}
#endif

Пара #if 0 #endif, окружающая файл makefile, гарантирует, что препроцессор игнорирует этот раздел текста, а маркер EOF отмечает, что команда make должна прекратить синтаксический анализ.

Ответ 5

CINT:

CINT - это интерпретатор для C и С++ код. Это полезно, например, для ситуаций где быстрое развитие важнее времени выполнения. С помощью интерпретатор компиляции и ссылки цикл резко сокращается способствуя быстрому развитию. CINT делает программирование на C/С++ приятным даже для программистов с неполным рабочим временем.

Ответ 6

Возможно, вы захотите проверить ryanmjacobs/c, который был разработан для этого. Он действует как обертка вокруг вашего любимого компилятора.

#!/usr/bin/c
#include <stdio.h>

int main(void) {
    printf("Hello World!\n");
    return 0;
}

Самое приятное в использовании c заключается в том, что вы можете выбрать, какой компилятор вы хотите использовать, например

$ export CC=clang
$ export CC=gcc

Итак, вы получаете все свои любимые оптимизации! Убейте, что tcc -run!

Вы также можете добавить флаги компилятора в shebang, если они заканчиваются символами --:

#!/usr/bin/c -Wall -g -lncurses --
#include <ncurses.h>

int main(void) {
    initscr();
    /* ... */
    return 0;
}

c также использует $CFLAGS и $CPPFLAGS, если они также установлены.

Ответ 7

#!/usr/bin/env sh
tail -n +$(( $LINENO + 1 )) "$0" | cc -xc - && { ./a.out "[email protected]"; e="$?"; rm ./a.out; exit "$e"; }

#include <stdio.h>

int main(int argc, char const* argv[]) {
    printf("Hello world!\n");
    return 0;
}

Это правильно пересылает аргументы и код выхода.

Ответ 8

Довольно короткое предложение будет использовать:

  • Текущий сценарий оболочки является интерпретатором по умолчанию для неизвестных типов (без шебанга или распознаваемого двоичного заголовка).
  • "#" - это комментарий в оболочке, а "#if 0" - код отключения.

    #if 0
    F="$(dirname $0)/.$(basename $0).bin"
    [ ! -f $F  -o  $F -ot $0 ] && { c++ "$0" -o "$F" || exit 1 ; }
    exec "$F" "[email protected]"
    #endif
    
    // Here starts my C++ program :)
    #include <iostream>
    #include <unistd.h>
    
    using namespace std;
    
    int main(int argc, char **argv) {
        if (argv[1])
             clog << "Hello " << argv[1] << endl;
        else
            clog << "hello world" << endl;
    }
    

Затем вы можете chmod +x свои .cpp файлы, а затем ./run.cpp.

  • Вы можете легко указать флаги для компилятора.
  • Двоичный файл кэшируется в текущем каталоге вместе с источником и обновляется при необходимости.
  • Исходные аргументы передаются в двоичный файл: ./run.cpp Hi
  • Он не использует a.out, так что вы можете иметь несколько двоичных файлов в одной папке.
  • Использует любой компилятор c++, который есть в вашей системе.
  • Двоичный файл начинается с "." так что он скрыт от списка каталогов.

Проблемы:

  • Что происходит при одновременных казнях?

Ответ 9

Вариант Джона Кугельмана можно написать следующим образом:

#!/bin/bash
t=`mktemp`
sed '1,/^\/\/code/d' "$0" | g++ -o "$t" -x c++ - && "$t" "[email protected]"
r=$?
rm -f "$t"
exit $r


//code
#include <stdio.h>

int main() {
    printf("Hi\n");
    return 0;
}

Ответ 10

Здесь еще одна альтернатива:

#if 0
TMP=$(mktemp -d)
cc -o ${TMP}/a.out ${0} && ${TMP}/a.out ${@:1} ; RV=${?}
rm -rf ${TMP}
exit ${RV}
#endif

#include <stdio.h>

int main(int argc, char *argv[])
{
  printf("Hello world\n");
  return 0;
}

Ответ 11

Я знаю, что этот вопрос не недавний, но я все равно решил добавить свой ответ. С Clang и LLVM нет необходимости записывать промежуточный файл или вызывать внешнюю вспомогательную программу/скрипт. (кроме clang/clang++/lli)

Вы можете просто передать вывод clang/clang++ на lli.

#if 0
CXX=clang++
CXXFLAGS="-O2 -Wall -Werror -std=c++17"
CXXARGS="-xc++ -emit-llvm -c -o -"
CXXCMD="$CXX $CXXFLAGS $CXXARGS $0"
LLICMD="lli -force-interpreter -fake-argv0=$0 -"
$CXXCMD | $LLICMD "[email protected]" ; exit $?
#endif

#include <cstdio>

int main (int argc, char **argv) {
  printf ("Hello llvm: %d\n", argc);
  for (auto i = 0; i < argc; i++) {
    printf("%d: %s\n", i, argv[i]);
  }
  return 3==argc;
}

Однако вышесказанное не позволяет использовать stdin в вашем скрипте c/c++. Если bash является вашей оболочкой, вы можете сделать следующее, чтобы использовать stdin:

#if 0
CXX=clang++
CXXFLAGS="-O2 -Wall -Werror -std=c++17"
CXXARGS="-xc++ -emit-llvm -c -o -"
CXXCMD="$CXX $CXXFLAGS $CXXARGS $0"
LLICMD="lli -force-interpreter -fake-argv0=$0"
exec $LLICMD <($CXXCMD) "[email protected]"
#endif

#include <cstdio>

int main (int argc, char **argv) {
  printf ("Hello llvm: %d\n", argc);
  for (auto i = 0; i < argc; i++) {
    printf("%d: %s\n", i, argv[i]);
  }
  for (int c; EOF != (c=getchar()); putchar(c));
  return 3==argc;
}