【问题标题】:Shell Script with C Preprocessor directive带有 C 预处理器指令的 Shell 脚本
【发布时间】:2014-08-13 14:59:17
【问题描述】:

我想在 shell 脚本中使用 C 预处理器指令。由于 C 预处理阶段不会编译整个脚本。因此应该可以使用 C 预处理器指令,因为它将被视为脚本的注释。在项目中维护一个包含多个更改并在宏下合并的单个副本也会很有帮助。

这是我为说明而编写的示例代码:

#ifdef HELLO
foo="Hello"
#else
foo="World"
#endif
echo $foo

现在我将此文件保存为testScript.c 并使用 gcc 进行编译

gcc -E testScript.c -o testScript.sh -DHELLO

现在我有testScript.sh。 如果我运行这个脚本,我得到的结果是

sh testScript.sh
Output: Hello

【问题讨论】:

  • 你的问题是?
  • 普通条件/变量有什么问题?
  • 我不会调用输入文件testScript.c,因为它绝对不是C源代码。
  • 条件变量没有问题,但您可以动态包含您的条件。这就是它的全部意义所在。
  • 由于您还没有更新您的问题,我再次提一下:您似乎没有问题。你已经展示了一种技术,但你实际上并没有问任何关于它的问题。你到底想问什么?

标签: c shell gcc


【解决方案1】:

您提出的建议是可能的,但通常不会完成,因为 shell 本身提供了比 C 预处理器更灵活和动态的功能。

您可以对环境变量执行操作:

case $HELLO in
    '' ) foo="World" ;;
    * ) foo="Hello" ;;
esac
echo "$foo" # Note quoting

甚至只是

echo "${HELLO+Hello}${HELLO-World}"

您可以指定默认值:

: ${HELLO=Hello}

如果未设置某些内容,您可以抛出错误:

: ${HELLO?Need a greeting}

总而言之,除非您在 C 语言确实需要您全神贯注的环境中工作,否则我的简单建议是学习使用 shell。

【讨论】:

  • 在另一种情况下,编写 C 程序而不是 shell 脚本。跟隔壁的COBOL奴隶打个招呼。抱歉,无法抗拒。
  • "你问的是可能的" -- 你在里面看到了一个问题?我没有。
  • tripleee 你为什么这么激进,这只是一种方法。我还是初学者。错误发生了。
  • 对不起,我当然不是故意的。如果您不喜欢我的评论,我很乐意将其删除——这只是为了幽默。
  • 您的方法在运行时执行代码。预处理技巧在构建时运行。我正在考虑定制一组安装在嵌入式系统上的 shell 脚本。它们中的大多数在不同的目标变体中是相同的,但这里和那里有一些差异。我只想为目标生成正确的脚本,而不是让它进行浪费的运行时间检查,比如“如果我在这个板上运行,就这样做”,跳过占用空间但永远不会执行的代码。跨度>
【解决方案2】:

这应该可以正常工作,实际上您根本不需要将测试脚本命名为 testScript.c。只需将其保留为 testScript.sh 并运行:

cpp testScript.sh

在其上运行 c 预处理器。

这应该比直接运行 gcc 更便携,因为除了 gcc 之外还有其他可用的 c 预处理器。

【讨论】:

    【解决方案3】:

    专门使用该预处理器的问题是 shell 脚本使用 # 作为注释字符。所以你不能用它来预处理有 cmets 的 shell 脚本。

    如果您可以遵守编码约定,即预处理的 shell 脚本仅使用 /* ... */// cmets,并且输出具有 no cmets,那可能会很好。

    不能删除的注释是 hash-bang 行。如果你将带有 hash bang 行的代码传递给 GNU C 预处理器,它会报错一个无效的预处理指令。

    解决这些问题的方法可能是采用如下约定:

    cpp -E ... -DHASH='#'
    

    也就是说,假设有一个名为HASH 的预定义宏扩展为井号。然后在脚本中你可以这样做:

    HASH!/bin/sh
    

    还可以像这样对 cme​​ts 进行编码:

    HASH This is a comment
    

    不幸的是,这并不完全有效,因为cpp 在行首插入了空格。我得到了输出:

     #!/bin/sh
    ^ space here, oops!
    
     # this is a comment
    

    所以必须解决这个问题。另一个问题是输出中有多余的措辞,如下所示:

    # 1 "prepro.sh.in"
    # 1 "<built-in>"
    # 1 "<command-line>"
    # 1 "/usr/include/stdc-predef.h" 1 3 4
    # 1 "<command-line>" 2
    # 1 "prepro.sh.in"
    

    必须清理掉。这是指向可行解决方案的内容。我的输入prepro.sh.in输入文件是这样的:

    HASH!/bin/sh
    
    HASH this is a comment
    
    foo()
    {
    }
    

    我正在运行的命令是这样的:

    cpp -E -DHASH='#' prepro.sh.in | sed -e '/^#/d;s/^ #/#/'
    

    输出:

    #!/bin/sh
    
    # this is a comment
    
    foo()
    {
    }
    

    我可能还有其他绊脚石。 C 预处理器不是定义为文本过滤器,而是定义为识别和生成“预处理标记”的处理步骤。甚至那些不是预处理指令的行也被标记化了。

    我会担心一些重要的空白没有被正确保留。

    GNU cpp 的手册页有一些劝诫性的话:

    C 预处理器仅适用于 C、C++ 和 Objective-C 源代码。过去曾被滥用为将军 文本处理器。它会阻塞不遵守 C 词法的输入 规则。例如,撇号将被解释为开头 字符常量,并导致错误。此外,你不能依赖它 保留对输入不重要的特征 C 族语言。如果对 Makefile 进行了预处理,则所有硬标签 将被删除,并且 Makefile 将不起作用。 话虽如此,您通常可以在以下事情上使用 cpp 不是 C。其他类似 Algol 的编程语言通常是安全的 (Pascal、Ada 等)组装也是如此,谨慎行事。 -传统-cpp 模式保留更多的空白,否则更宽容。 许多问题可以通过编写 C 或 C++ 风格的 cmets 来避免 而不是母语 cmets,并保持宏简单。

    如果您还是要这样做,最好遵循有用的建议来使用-traditional-cpp 选项。

    【讨论】:

    • 如果你正在考虑这个,也许看看其他宏处理器。我觉得 M4 很可怕,但它当然不需要这些变通方法。 gnu.org/software/m4/m4.html
    • @triplee M4 完全是垃圾。我采用了一种非常好的方法,我应该添加到答案中。我首先过滤文件以识别我想要激活的那些预处理指令。所有其他行都替换为其行号。结果被预处理。然后,由整数(行号)组成的所有行都被相应的原始行替换。这样,只处理条件#if-s。没有宏或其他东西;预处理器永远不会看到非 C 代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    相关资源
    最近更新 更多