0

For example, cat foo.c would print the whole file, cat foo.c | grep main will print the line where the main function is defined.

So how would I print the entire main function?

(I am on Ubuntu)

6
  • Why would you need to do that?
    – waltinator
    Jul 12, 2021 at 1:13
  • 2
    @waltinator Why not? Jul 12, 2021 at 1:17
  • I may just make a python script for this, If it works ill post it Jul 12, 2021 at 1:17
  • Do you need to keep comments? And do you need to expand, or not expand macros?
    – ibuprofen
    Jul 12, 2021 at 3:12
  • 1
    Is the file formatted in a conventional way? If so then print from main( to a line containing just }, e.g. sed -n '/main(/,/^}$/p' filename.
    – icarus
    Jul 12, 2021 at 3:25

2 Answers 2

2

Depends a lot on source and, control over source, desired result etc.

In it's simplest form:

sed -n '/^int main(/,/^}/p' file.c

That would print everything between lines starting with int main( until } inclusive.


If you need to expand macros you could use the c preprocessor cpp then run it trough indent and finally extract with sed. indent could in any case be OK to make sure the code is well formatted etc. Example:

  • cpp expands macros. (Could be cumbersome on a big code base.)
  • indent format the code for matching (indentation). The -bls options makes sure int and main is on same line.
  • sed extract the main part.
cpp file.c | 
indent --linux-style --standard-output -bls | 
sed -n '/^int main(/,/^}/p'

Or to mainly make sure indentation is OK:

indent --linux-style --standard-output -bls file.c | 
sed -n '/^int main(/,/^}/p'

Optionally add -fc1 to make comment blocks content not start in first column. (E.g. if a block comment has a line starting with int main(.)


Again; It all depends on how much control you have over the input, how much change you either want, do not want, need etc.


Example:

#include <stdio.h>

#define ANSWER(q, s) (q |= (s))
#define WHAT for

void foo(int a) {
    printf("%x\n", a | 2);
}

int

main(
    int argc,
    char *argv[])
{
    /* Some
     * comment
     * */
    int i, k = 40;
        ANSWER(k, 2);
    // Another comment
WHAT (i = 0; i < 3; ++i) {
    foo(i);
}
    return 0;
}

/*

int main(void) {
    return 0;
}

*/

Result (using cpp, indent, sed):

int main(int argc, char *argv[])
{

    int i, k = 40;
    (k |= (2));

    for (i = 0; i < 3; ++i) {
        foo(i);
    }
    return 0;
}

If you need to find the line where main start, or any function for that matter, one option is also ctags, i.e:

$ ctags -x --c-kinds=f test.c | awk '$1 == "main"'

Have seen both c-kinds and c-types and both work, but kinds is the only one I have in manual. Perhaps something to do with ctags vs exuberant ctags.

0

If you don't want to reformat the C source with indent and cpp (because you want to see it in its original, unmodified glory/hideousness), you could use perl's Regexp::Common module. For example, using example.c from @ibuprofen's answer:

$ perl -MRegexp::Common=balanced -0 -n -e \
    'm/(^\s*(?:int\s+)?\s*main\([^)]*\)\s*$RE{balanced}{-parens=>"{}"}\s*)/m
       and print $1' example.c
int

main(
    int argc,
    char *argv[])
{
    /* Some
     * comment
     * */
    int i, k = 40;
        ANSWER(k, 2);
    // Another comment
WHAT (i = 0; i < 3; ++i) {
    foo(i);
}
    return 0;
}

This captures and prints the first regex pattern matching the definition of a main function, if it's present in the input.

The m at the beginning of the m/.../m operation is the perl regexp match operator. It's optional if you use the "standard" / delimiter, but required if you use a different delimiter (like = or : or one of the balanced delimiters like {..}). I like to use it even where it's not required.

The m at the end of the m/.../m is perl's multi-line string regex modifier, so that the regex can match across line boundaries.

The \s* at the end of the regex capture ensures that it captures any whitespace (including LF or CR+LF) at the end of the function's closing }.

or, same thing in a standalone script instead of a one-liner:

#!/usr/bin/perl

undef $/; # enable slurp mode (read entire input at once)

use Regexp::Common qw(balanced);

while(<>) {
  m/(^\s*(?:int\s+)?\s*main\([^)]*\)\s*$RE{balanced}{-parens=>'{}'}\s*)/m
    and print $1;
};

This would also work pretty well instead of sed when combined with indent and cpp. Which would also resolve the bug below (but there would be no real advantage over using sed, unless you also needed to use other Regexp::Common patterns like matching/extracting C comments, or other perl features):

BUGS: this will print the first main function found, whether it is commented out or not.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .