所以这可能是一个很长的镜头,但有没有办法将C或C ++文件作为脚本运行?我试过了:
#!/usr/bin/gcc main.c -o main; ./main
int main(){ return 0; }
但它说:
./main.c:1:2: error: invalid preprocessing directive #!
答案 0 :(得分:26)
对于C,您可以查看Tiny C编译器tcc。将C代码作为脚本运行是其可能的用途之一。
答案 1 :(得分:20)
简短回答:
//usr/bin/clang "$0" && exec ./a.out "$@"
int main(){
return 0;
}
诀窍是你的文本文件必须是有效的C / C ++代码和shell脚本。在解释器到达C / C ++代码之前,请记住shell脚本中的exit
,或者调用exec
magic。
使用chmod +x main.c; ./main.c
运行。
不需要像#!/usr/bin/tcc -run
这样的shebang,因为类似unix的系统已经在shell中执行了文本文件。
(改编自this comment)
我在我的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;
}
如果您的编译行增长太多,您可以使用预处理器(改编自this answer),因为这个普通的旧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程序员只需将一个shebang放在文本文件的开头,而不会破坏语法:
#!/usr/bin/rdmd
void main(){}
答案 2 :(得分:17)
$ cat /usr/local/bin/runc
#!/bin/bash
sed -n '2,$p' "$@" | 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
文件并剥离hash-bang行。 2,$p
表示将第2行打印到文件末尾; "$@"
扩展为runc脚本的命令行参数,即"main.c"
。
sed的输出通过管道传输到gcc。将-
传递给gcc告诉它从stdin读取,当你这样做时,你还必须用-x
指定源语言,因为它没有可以猜出的文件名。
答案 3 :(得分:10)
由于shebang行将传递给编译器,而#表示预处理器指令,它将阻塞#!.
您可以做的是将makefile嵌入.c文件中(如this xkcd thread中所述)
#if 0
make $@ -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
makefile周围的#if 0 #endif
对确保预处理器忽略该部分文本,EOF标记标记make命令应停止解析输入的位置。
答案 4 :(得分:7)
CINT:
CINT是C和C ++的解释器 码。它是有用的,例如对于情况 快速发展的地方更多 比执行时间重要。运用 解释器的编译和链接 周期大大减少 促进快速发展。 CINT 使C / C ++编程更加愉快 适合兼职程序员。
答案 5 :(得分:5)
您可能想要结帐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
。
答案 6 :(得分:5)
#!/usr/bin/env sh
tail -n +$(( $LINENO + 1 )) "$0" | cc -xc - && { ./a.out "$@"; e="$?"; rm ./a.out; exit "$e"; }
#include <stdio.h>
int main(int argc, char const* argv[]) {
printf("Hello world!\n");
return 0;
}
这也正确地转发了参数和退出代码。
答案 7 :(得分:3)
约翰库格曼的变体可以用这种方式写成:
#!/bin/bash
t=`mktemp`
sed '1,/^\/\/code/d' "$0" | g++ -o "$t" -x c++ - && "$t" "$@"
r=$?
rm -f "$t"
exit $r
//code
#include <stdio.h>
int main() {
printf("Hi\n");
return 0;
}
答案 8 :(得分:3)
一个简短的建议会利用:
“#”是shell中的注释,“#if 0”是禁用代码。
#if 0
F="$(dirname $0)/.$(basename $0).bin"
[ ! -f $F -o $F -ot $0 ] && { c++ "$0" -o "$F" || exit 1 ; }
exec "$F" "$@"
#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
{em} .cpp 文件,然后./run.cpp
。
./run.cpp Hi
a.out
,因此您可以在同一文件夹中拥有多个二进制文件。问题:
答案 9 :(得分:2)
这是另一种选择:
#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;
}
答案 10 :(得分:1)
我知道这个问题不是最近才提出来的,但是我还是决定把我的答案混在一起。 使用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 "$@" ; 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;
}
但是,以上内容不允许您在c / c ++脚本中使用stdin。 如果bash是您的shell,则可以执行以下操作以使用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) "$@"
#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;
}
答案 11 :(得分:1)
有几个地方建议应该保留shebang(#!),但对于gcc编译器来说,它是非法的。因此有几种解决方案可以解决。另外,可以插入预处理指令,以在c代码错误的情况下修复编译器消息。
#!/bin/bash
#ifdef 0
xxx=$(mktemp -d)
awk 'BEGIN
{ print "#line 2 \"$0\""; first=1; }
{ if (first) first=0; else print $0 }' $0 |\
g++ -x c++ -o ${xxx} - && ./${xxx} "$@"
rv=$?
\rm ./${xxx}
exit $rv
#endif
#include <iostream>
int main(int argc,char *argv[]) {
std::cout<<"Hello world"<<std::endl;
}
答案 12 :(得分:0)
如上一个答案所述,如果您使用tcc
作为编译器,则可以将shebang #!/usr/bin/tcc -run
作为源文件的第一行。
但是,这有一个小问题:如果要编译同一文件,gcc
将抛出error: invalid preprocessing directive #!
(tcc
将忽略shebang并进行编译就可以了) 。
如果仍然需要使用gcc
进行编译,一种解决方法是使用tail
命令从源文件中切断shebang行,然后再将其传递到gcc
中:
tail -n+2 helloworld.c | gcc -xc -
请记住,所有警告和/或错误将只用一行显示。
您可以通过创建一个bash脚本来自动化该操作,该脚本检查文件是否以shebang开头,例如
if [[ $(head -c2 $1) == '#!' ]]
then
tail -n+2 $1 | gcc -xc -
else
gcc $1
fi
并使用它来编译源代码,而不是直接调用gcc
。
答案 13 :(得分:0)
只是想分享一下,感谢 Pedro 对使用 #if 0
技巧的解决方案的解释,我已经更新了我在 TCC (Sugar C) 上的 fork,以便所有示例都可以用 shebang 调用,最后,当在 IDE 上查找源代码。
现在,代码可以在 VS Code 中使用 clangd
漂亮地显示项目源代码。示例第一行如下所示:
#if 0
/usr/local/bin/sugar `basename $0` $@ && exit;
// above is a shebang hack, so you can run: ./args.c <arg 1> <arg 2> <arg N>
#endif
这个项目的初衷一直是使用 C 语言,就像使用底层 TCC 的脚本语言一样,但是客户端的优先级是 ram 输出而不是文件输出(没有 -run
指令)。
您可以通过以下网址查看项目:https://github.com/antonioprates/sugar