3

I want to use C preprocessor directive with shell script. Since C preprocessing stage does not compile the entire script. Thus it should be possible to use C preprocessor directives as it would be considered as comment for the script. It would be helpful as well in a project to maintain a single copy with multiple changes incorporated under the macro.

Here is sample code I wrote for illustration:

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

Now I would save this file as testScript.c and compile with gcc

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

And now I have testScript.sh with me. If I run this script I get the result as

sh testScript.sh
Output: Hello
7
  • 9
    And your question is ? Commented Aug 13, 2014 at 15:06
  • 1
    What's wrong with normal conditionals/variables? Commented Aug 13, 2014 at 15:19
  • I wouldn't call the input file testScript.c, because it definitely isn't C source code. Commented Aug 13, 2014 at 15:41
  • Nothings wrong with conditional variable but you can include your conditions dynamically. That's what it is all about. Commented Aug 13, 2014 at 15:53
  • 1
    Since you haven't updated your question, I'll mention this again: You do not appear to have a question. You've demonstrated a technique, but you're not actually asking anything about it. What exactly are you trying to ask? Commented Aug 13, 2014 at 17:41

3 Answers 3

5

What you are proposing is possible, but isn't usually done, because the shell itself provides far more flexible and dynamic features than the C preprocessor.

You can take action on an environment variable:

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

or even just

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

You can specify default values:

: ${HELLO=Hello}

You can throw an error if something is unset:

: ${HELLO?Need a greeting}

In summary, unless you work in an environment where C really requires all of your attention, my simple recommendation would be to learn to use the shell.

Sign up to request clarification or add additional context in comments.

6 Comments

In the other case, write a C program instead of a shell script. Say hello to the COBOL slaves next door. Sorry, couldn't resist.
"What you are asking is possible" -- You saw a question in there? I didn't.
tripleee why are you getting so aggressive, it was just an approach. I'm beginner still. Mistakes happen.
I'm sorry, I certainly did not mean to be aggressive. If you dislike my comment, I'll be happy to remove it -- it was only meant as an attempt at humor.
Your approach executes code at run time. The preprocessing trick runs at build time. I'm looking at customizing a body of shell scripts that get installed on an embedded system. Most of them are the same among different target variants, but there are a few differences here and there. I just want to generate the right script for the target and not have it do wasteful run time checks like "if I'm running on this board, do that", skipping around code that takes up space but will never be executed.
|
1

This should work fine, in fact you do not need to name the testscript testScript.c at all. Just keep it as testScript.sh and run:

cpp testScript.sh

to run the c preprocessor on it.

This should be more portable than running gcc directly as there are other c preprocessors available other than gcc.

Comments

0

The problem with using that preprocessor specifically is that shell scripts use # as a comment character. So you cannot use it to preprocess shell scripts which have comments.

That may be fine if you you can stick to a coding convention that your preprocessed shell scripts use only /* ... */ and // comments, and the output has no comments.

A comment that cannot be removed is the hash-bang line. If you pass code with the hash bang line to the GNU C preprocessor, it will complain about an invalid preprocessing directive.

A solution to these problems may be to adopt a convention such as the following:

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

That is to say, assume there is a predefined macro called HASH which expands to the hash mark. Then in the script you can do:

HASH!/bin/sh

and also encode comments like this:

HASH This is a comment

Unfortunately, this doesn't quite work, because cpp inserts whitespace at the start of the line. I get the output:

 #!/bin/sh
^ space here, oops!

 # this is a comment

So that has to be addressed. Another problem is that there is extra verbiage in the output like this:

# 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"

That has to be cleaned out. Here is something that points toward a viable solution. My input prepro.sh.in input file is this:

HASH!/bin/sh

HASH this is a comment

foo()
{
}

The command I'm running is this:

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

The output:

#!/bin/sh

# this is a comment

foo()
{
}

There may me other stumbling blocks. The C preprocessor is defined not as a textual filter but as a processing step which identifies and generates "preprocessing tokens". Even the lines which are not preprocessing directives are being tokenized.

I would be concerned about some instances of significant whitespace not being correctly preserved.

The man page for GNU cpp has some admonishing words:

   The C preprocessor is intended to be used only with C, C++, and
   Objective-C source code.  In the past, it has been abused as a general
   text processor.  It will choke on input which does not obey C's lexical
   rules.  For example, apostrophes will be interpreted as the beginning
   of character constants, and cause errors.  Also, you cannot rely on it
   preserving characteristics of the input which are not significant to
   C-family languages.  If a Makefile is preprocessed, all the hard tabs
   will be removed, and the Makefile will not work.

   Having said that, you can often get away with using cpp on things which
   are not C.  Other Algol-ish programming languages are often safe
   (Pascal, Ada, etc.) So is assembly, with caution.  -traditional-cpp
   mode preserves more white space, and is otherwise more permissive.
   Many of the problems can be avoided by writing C or C++ style comments
   instead of native language comments, and keeping macros simple.

If you're going to do this anyway, it's probably a good idea to follow the useful recommendation to use the -traditional-cpp option.

2 Comments

If you are considering this, maybe look at other macro processors. I find M4 horrible but it certainly doesn't require these kinds of workarounds. gnu.org/software/m4/m4.html
@triplee M4 is complete garbage. I went for a very nice approach I should add to the answer. I first filter the file to identify those preprocessing directives that I want to be active. All other lines are replaced with their line number. The result is preprocessed. Then, all lines which consist of an integer (line number) are replaced by the corresponding original line. This way, only the conditional #if-s are processed. No macros or other stuff; the preprocessor never sees the non-C code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.