Run C or C++ file as a script

2020-02-07 17:56发布

So this is probably a long shot, but is there any way to run a C or C++ file as a script? I tried:

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

int main(){ return 0; }

But it says:

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

标签: c++ c shell
12条回答
戒情不戒烟
2楼-- · 2020-02-07 18:15

Variatn of John Kugelman can be written in this way:

#!/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;
}
查看更多
够拽才男人
3楼-- · 2020-02-07 18:16

Since the shebang line will be passed to the compiler, and # indicates a preprocessor directive, it will choke on a #!.

What you can do is embed the makefile in the .c file (as discussed in 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

The #if 0 #endif pair surrounding the makefile ensure the preprocessor ignores that section of text, and the EOF marker marks where the make command should stop parsing input.

查看更多
冷血范
4楼-- · 2020-02-07 18:18
#!/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;
}

This properly forwards the arguments and the exit code too.

查看更多
姐就是有狂的资本
5楼-- · 2020-02-07 18:19

I know this question is not a recent one, but I decided to throw my answer into the mix anyways. With Clang and LLVM, there is not any need to write out an intermediate file or call an external helper program/script. (apart from clang/clang++/lli)

You can just pipe the output of clang/clang++ to 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;
}

The above however does not let you use stdin in your c/c++ script. If bash is your shell, then you can do the following to use 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;
}
查看更多
走好不送
6楼-- · 2020-02-07 18:21

As stated in a previous answer, if you use tcc as your compiler, you can put a shebang #!/usr/bin/tcc -run as the first line of your source file.

However, there is a small problem with that: if you want to compile that same file, gcc will throw an error: invalid preprocessing directive #! (tcc will ignore the shebang and compile just fine).

If you still need to compile with gcc one workaround is to use the tail command to cut off the shebang line from the source file before piping it into gcc:

tail -n+2 helloworld.c | gcc -xc -

Keep in mind that all warnings and/or errors will be off by one line.

You can automate that by creating a bash script that checks whether a file begins with a shebang, something like

if [[ $(head -c2 $1) == '#!' ]]
then
  tail -n+2 $1 | gcc -xc -
else
  gcc $1
fi

and use that to compile your source instead of directly invoking gcc.

查看更多
Emotional °昔
7楼-- · 2020-02-07 18:23

Short answer:

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

The trick is that your text file must be both valid C/C++ code and shell script. Remember to exit from the shell script before the interpreter reaches the C/C++ code, or invoke exec magic.

Run with chmod +x main.c; ./main.c.

A shebang like #!/usr/bin/tcc -run isn't needed because unix-like systems will already execute the text file within the shell.

(adapted from this comment)


I used it in my C++ script:

//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;
}

If your compilation line grows too much you can use the preprocessor (adapted from this answer) as this plain old C code shows:

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

Of course you can cache the executable:

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

Now, for the truly eccentric Java developer:

/*/../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 programmers simply put a shebang at the beginning of text file without breaking the syntax:

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

See:

查看更多
登录 后发表回答