Итак, это, вероятно, длинный снимок, но есть ли способ запустить файл C или С++ как script? Я пробовал:
#!/usr/bin/gcc main.c -o main; ./main
int main(){ return 0; }
Но он говорит:
./main.c:1:2: error: invalid preprocessing directive #!
Итак, это, вероятно, длинный снимок, но есть ли способ запустить файл C или С++ как script? Я пробовал:
#!/usr/bin/gcc main.c -o main; ./main
int main(){ return 0; }
Но он говорит:
./main.c:1:2: error: invalid preprocessing directive #!
Для C вы можете посмотреть tcc, Tiny C Compiler. Выполнение кода C как script является одним из возможных вариантов использования.
Краткий ответ:
//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(){}
Видеть:
$ 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
, поскольку у него нет имени файла для угадывания.
Так как строка 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 должна прекратить синтаксический анализ.
CINT:
CINT - это интерпретатор для C и С++ код. Это полезно, например, для ситуаций где быстрое развитие важнее времени выполнения. С помощью интерпретатор компиляции и ссылки цикл резко сокращается способствуя быстрому развитию. CINT делает программирование на C/С++ приятным даже для программистов с неполным рабочим временем.
Возможно, вы захотите проверить 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
, если они также установлены.
#!/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;
}
Это правильно пересылает аргументы и код выхода.
Довольно короткое предложение будет использовать:
"#" - это комментарий в оболочке, а "#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
, так что вы можете иметь несколько двоичных файлов в одной папке.Проблемы:
Вариант Джона Кугельмана можно написать следующим образом:
#!/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;
}
Здесь еще одна альтернатива:
#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;
}
Я знаю, что этот вопрос не недавний, но я все равно решил добавить свой ответ. С 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;
}