簡體   English   中英

將 C 或 C++ 文件作為腳本運行

[英]Run C or C++ file as a script

所以這可能是一個很長的鏡頭,但是有沒有辦法將 C 或 C++ 文件作為腳本運行? 我試過:

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

int main(){ return 0; }

但它說:

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

簡短的回答:

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

訣竅是您的文本文件必須是有效的 C/C++ 代碼和 shell 腳本。 記住在解釋器到達 C/C++ 代碼之前exit shell 腳本,或者調用exec魔法。

使用chmod +x main.c; ./main.c運行chmod +x main.c; ./main.c chmod +x main.c; ./main.c

不需要像#!/usr/bin/tcc -run這樣的shebang,因為類unix 系統已經在shell 中執行文本文件。

(改編自此評論


我在我的 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 程序員只需在文本文件的開頭放置一個shebang,而不會破壞語法:

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

看:

對於 C,您可以查看tcc ,Tiny C 編譯器。 將 C 代碼作為腳本運行是其可能的用途之一。

$ 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 告訴它從標准輸入讀取,當你這樣做時,你還必須用-x指定源語言,因為它沒有文件名可以猜測。

由於shebang 行將傳遞給編譯器,並且# 表示預處理器指令,因此它將在#! 上阻塞。

您可以做的是將 makefile 嵌入到 .c 文件中(如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 命令應該停止解析輸入的位置。

CINT :

CINT 是 C 和 C++ 代碼的解釋器。 例如,對於快速開發比執行時間更重要的情況,它很有用。 使用解釋器可以大大減少編譯和鏈接周期,從而促進快速開發。 即使是兼職程序員,CINT 也使 C/C++ 編程變得愉快。

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

這也正確轉發了參數和退出代碼。

您可能想要查看為此而設計的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如果它們也被設置)。

一個相當簡短的提議將利用:

  • 當前的 shell 腳本是未知類型的默認解釋器(沒有 shebang 或可識別的二進制頭)。
  • “#”是 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你的.cpp文件,然后./run.cpp

  • 您可以輕松地為編譯器提供標志。
  • 二進制文件與源文件一起緩存在當前目錄中,並在必要時更新。
  • 原始參數傳遞給二進制文件: ./run.cpp Hi
  • 它不會重用a.out ,因此您可以在同一文件夾中擁有多個二進制文件。
  • 使用您系統中的任何 C++ 編譯器。
  • 二進制文件以“.”開頭。 以便它從目錄列表中隱藏。

問題:

  • 並發執行會發生什么?

John Kugelman 的變體可以這樣寫:

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

這是另一種選擇:

#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 "$@" ; 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;
}

有幾個地方建議 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; 
}

如之前的回答所述,如果您使用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

只是想分享一下,感謝 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 輸出而不是文件輸出(沒有 of -run指令)。

您可以在以下位置查看該項目: https : //github.com/antonioprates/sugar

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM