【问题标题】:pipeline echo to gcc?管道回显到 gcc?
【发布时间】:2017-12-27 23:30:06
【问题描述】:

从我使用的终端调用 C 中的printf("Hello!");

echo '#include<stdio.h>
void main()
{
printf("Hello!");
}' > foo.c

然后调用gcc foo.c 进行输出。不幸的是,流水线

echo '#include<stdio.h>
void main()
{
printf("Hello!");
}' | gcc 

抱怨没有输入文件失败。最终,我想要一个脚本,我可以在其中使用 ./script [command] 从终端编译 C command。任何建议将不胜感激。

【问题讨论】:

  • 请问您什么时候想这样做?我看不出它有什么用处。
  • @klutt:如果 C 代码是由某个程序生成的,您可能想要这样做。但确实,这不值得痛苦
  • @klutt 一般来说,管道是一件好事。一般来说,任何程序都应该接受来自标准输入的输入,除非它有很好的理由不这样做。仅仅因为你想不出它的好用处,并不意味着拥有通用能力不是一个好主意。 (现在,话虽如此,我同意很难想到您实际上需要将 C 代码通过管道传输到 C 编译器中的情况。我实现了符号链接技巧 - 如答案中所述 - 许多年前,只是为了好玩,但我认为我从未真正使用过它。)
  • 可能不是 OP 想要的,但是在 shell 脚本等中“即时”执行小块 C 的另一种好方法是,如果你碰巧有一个 C 口译员。例如,有这样的解释器,我只需输入ci -c 'printf("Hello!\n");'

标签: c gcc compilation pipe


【解决方案1】:

可以,但您必须使用-x 选项指定语言。使用-xc 将输入文件指定为标准输入,将语言指定为C(如果您希望它是C++,请使用-xc++)。所以在你的情况下,命令是

echo '#include<stdio.h>
void main()
{
printf("Hello!");
}' | gcc -o output.o -xc -

您可以阅读有关Command Line Compiler Arguments 的更多信息:请参阅Invoking GCC 章节。


然而,正如@Basile 所说,不值得努力避免处理 C 文件Check his answer for more information.

【讨论】:

    【解决方案2】:

    Other 的答案正在解释如何让gccstdin 读取 C 代码,但请注意,在实践中
    不值得努力避免处理 C文件

    (我猜你正在使用一些 POSIX 操作系统,比如 Linux;如果没有,请根据你的操作系统和编译器调整我的答案)

    自动发出 C 代码是通常的做法(许多学术编程语言是 compiling to C,许多构建使用生成的 C 代码,例如来自 bison)。但是使用管道和 stdin 并没有太大的好处,因为 C 编译器需要很多时间(尤其是 optimization,这是您经常想要的,例如通过使用 gcc -O2 编译)。

    所以我的建议是将生成的 C 代码放在一个真正的文件中(也许是一些临时的,以后应该适当地删除)。如果磁盘性能是一个问题,该文件可能位于某个 tmpfs 文件系统上(实际上它永远不会出现在 gcc 编译的源代码中),并且如果您愿意,该文件可能是临时的(例如稍后删除)。

    请注意,gcc 程序通常在其内部使用临时文件(例如,用于生成的汇编程序文件或目标文件)。如果您将 -v 添加到您的 gcc 编译命令,您将看到它们,例如如果你用gcc -v foo.c -o fooprog 编译foo.c。所以没有实际理由避免使用 C 源文件(因为 gcc 无论如何都使用临时文件!)。

    如果您想在正在运行的程序中动态生成代码,您可以生成临时 C 代码,然后将其编译为临时 plugin(使用 position-independent code)然后编译为 dynamically load 该插件(例如使用 dlopen(3) &dlsym(3) 在 Linux 上);但是您也可以使用JIT compiling 库,例如GCCJITLLVMasmjit 等....当然这需要您的operating system 的一些支持(例如,增加您的virtual address space):纯标准C11(读取n1570),由于在translation units(组成您的程序的.c文件集)的联合中定义,函数集在构建时固定,并且您无法在运行时生成代码.

    如果你想在一些shell script中生成一些C程序,最好使用临时文件,例如使用tempfilemktemp 等...(并在EXITTERM 信号上使用trap builtin 以在脚本终止时删除临时文件)。

    如果您在 build 中生成一些临时 C 代码,则可以将好的 build automation 程序(例如 GNU makeninja)配置为删除这些临时 C 文件(例如阅读关于 intermediate files 的 @987654367 @)。

    还可以查看homoiconic 编程语言,例如 Common Lisp。例如SBCL 在每次REPL 交互时都会编译成机器码。

    【讨论】:

    • 从标准输入编译 C 代码确实是不切实际的,但是在这里如此激烈地反对这种可能性时,我不禁注意到你已经非常强烈地反对管道的概念!我自己对 Unix Philosophy 的阅读(诚然有时过于狂热)认为您应该总是能够管道传输,您应该永远不得不求助于临时文件. (现在,话虽如此,如果一个“违反” Unix 哲学的 C 编译器是由最初建立该哲学的同一个人编写的,显然我不会抱怨太多。)
    【解决方案3】:

    我过去做过类似的事情

    ln -s /dev/stdin stdin.c
    

    现在stdin.c/dev/stdin 的符号链接,即“包含”我输入或通过管道传输的任何内容的魔法文件。但文件名以.c 结尾,因此任何C 编译器都应将其视为C 文件。像

    (echo '#include <stdio.h>'
     echo 'int main(void) { printf("Hello, world!\n"); }') |
        cc stdin.c
    

    工作正常。

    (显然,这假设您使用的是带有符号链接和/dev/stdin 的类 Unix 系统。)


    附录:显然这也假设您使用的是一次性 C 编译器!我相信我首先使用/dev/tty 和一个较旧的两遍编译器(可能是 Ritchie 的原始编译器,虽然我不再记得了)尝试了这个技巧,只要你输入相同的输入,它在那里也“工作得很好”两次。 :-)(说真的。main(){printf("Hello, world!\n");}^Dmain(){printf("Hello, world!\n");},中间有一个 control-D。)

    【讨论】:

      【解决方案4】:

      无需回显管道,您可以这样做:

      gcc -x c - <<eof
      #include<stdio.h>
      void main()
      {
      printf("Hello!");
      }
      eof
      

      【讨论】:

        猜你喜欢
        • 2019-01-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-30
        • 2015-11-12
        • 2013-09-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多