1

I am new to Shell scripting. I wish to write a Unix script which will call a C program for N= 2^{i}, i= 1,2 ....20; and later record this data in a file.

(This program calculates definite integral using trapezoid rule in C and returns iterative result and error for each term N.)

While I just started learning C, I wrote my code for trapezoid rule:

#include<stdio.h>

#include<math.h>
#define PI 3.14159265358979323846

float fn(float x)
{ 
  float integrand;
  integrand = (1.0/(1.0+x*x));
  return integrand;
}
int main()
{
  int i,N;
  float a,b,sum=0,result=0,h;
  float error;


  printf("Enter the no of equally spaced points =");
  scanf("%d",&N);
  printf("Enter the lower limit=");
  scanf("%f",&a);
  printf("Enter the upper limit=");
  scanf("%f",&b);
  h=(b-a)/(N-1);
  for(i=1;i<=N;i++)
  {
    sum=sum+fn(a+i*h);
    result=(fn(a)+fn(b)+2*sum)*h/2;
    error = fabs((atan(b)-atan(a))-result);
    //error = PI/2.0 - result;

    printf("N=%d result=%f error=%f\n", i, result, error);
  }
  printf("final result =%f\n", result);
  printf("cumulative error =%f\n", error);

}

I execute this code by

gcc -o err.o trap_error.c -lm

and my gcc version is gcc (Ubuntu 5.4.0-6ubuntu1~16.04.2) 5.4.0 20160609

I searched over Internet randomly, but didn't find useful info, I think I also have to modify my code too. If you just help me write the Unix script and later redirect the output in a .txt file. A Unix script with lines explained will be very helpful.

  • 2
    You can't directly call a C function from a script, but if you compile your C program and execute it from a script, the function main() will get called. If you want your C program to take input from the script, you can declare the main function as void main(int argc, char *argv[]) and call your script as myscript arg1 arg2 ...; then, the given arguments will be stored in the argv array. As for output, using printf() will print the results to stdout. You can redirect it using the > operator, e.g., myscript arg1 arg2 > out.txt. This question may be better suited for StackOverflow. – Malte Skoruppa Sep 22 '16 at 18:01
  • Also the command gcc -o err.o trap_error.c -lm doesn't "execute" your code, it compiles and links it, with the standard math library libm, to an executable file called err.o - to execute that, you'd need to give the command ./err.o. BTW the .o suffix is conventionally reserved for intermediate object code files rather than executable programs. – steeldriver Sep 22 '16 at 18:05
  • So you want a shell script that generates the N's as per N= 2^{i}, i= 1,2 ....20 and feeds each to an invocation of your program? But yourprogram takes 3 numbers, not 1. What will the lower a upper limit be? – PSkocik Sep 22 '16 at 18:10
  • The lower limit -1, your limit 1 – bhjghjh Sep 22 '16 at 18:12
  • @PSkocik, I saw you submitted an answer, which I can't see now, can you please repost that? I think that was very close what I needed – bhjghjh Sep 23 '16 at 1:11
1

You're compiling and linking, so the output will be an executable, not and object file, which makes the .o suffix misleading.

gcc -o err trap_error.c -lm

might be a better idea.

It's not very clear what you're asking, but it looks like you're trying to feed it some auto-generated input and redirect all outputs to a file. You can generate N= 2^{i}, i= 1,2 ....20; in bash with:

for ((i=2;i<2**20;i*=2)); do echo "$i" ; done

If -1 and 1 are going to be your lower and upper limit respectively, then you can add those after $i, and pipe each such triple to an invocation of your program:

for ((i=2;i<2**20;i*=2)); do echo "$i -1 1" | ./err ; done > out.txt

The > out.txt part will redirect all the outputs of all the ./err invocations into out.txt.

  • The bash-specific for (( )) is basically like a C for loop (i*=2 has the same semantics as in c) with the addition of ** which means exponentiation. – PSkocik Sep 22 '16 at 18:19
  • I'm not sure if this answers your question. – PSkocik Sep 22 '16 at 18:19
4

Instead of prompting users and reading the parameters from standard input, you should embrace the Unix philosophy, and use command-line parameters instead.

The following example is slightly long, because I wanted to show my preferred parameter checking functions. scanf() family of functions does not check for overflow, so strto*() needs to be used. Furthermore, there may occasionally be garbage after the number (say, '12l' -- last being letter L -- instead of '121'), which I personally want to catch.

#include <stdlib.h>
#include <locale.h>
#include <ctype.h>
#include <stdio.h>
#include <errno.h>

/* Helper function to parse a double.
 * Returns 0 if successful, nonzero otherwise.
*/
static int parse_double(const char *s, double *v)
{
    const char *end;
    double      val;

    if (!s)
        return errno = EINVAL;

    end = s;
    errno = 0;
    val = strtod(s, (char **)&end);
    if (errno)
        return errno;

    if (!end || end == s)
        return errno = EINVAL;

    while (*end != '\0' && isspace(*end))
        end++;

    if (*end != '\0')
        return errno = EINVAL;

    if (v)
        *v = val;

    return 0;
}

/* Helper function to parse a long.
 * Returns 0 if successful, nonzero otherwise.
*/
static int parse_long(const char *s, long *v)
{
    const char *end;
    long        val;

    if (!s)
        return errno = EINVAL;

    end = s;
    errno = 0;
    val = strtol(s, (char **)&end, 0);
    if (errno)
        return errno;

    if (!end || end == s)
        return errno = EINVAL;

    while (*end != '\0' && isspace(*end))
        end++;

    if (*end != '\0')
        return errno = EINVAL;

    if (v)
        *v = val;

    return 0;
}

Of above, parse_long() supports decimal (987), hexadecimal (0x3DB), and octal (01733) notation.

The main() is then something like

int main(int argc, char *argv[])
{
    double min, max;
    long   n;

    setlocale(LC_ALL, "");

    /* Require "command N min max" -- four parameters,
     * including the executable file name (argv[0]). */

    if (argc != 4 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
        fprintf(stderr, "       %s N min max\n", argv[0]);
        return EXIT_FAILURE;
    }

    if (parse_long(argv[1], &n) || n < 1L) {
        fprintf(stderr, "%s: Invalid N.\n", argv[1]);
        return EXIT_FAILURE;
    }

    if (parse_double(argv[2], &min)) {
        fprintf(stderr, "%s: Invalid minimum.\n", argv[2]);
        return EXIT_FAILURE;
    }

    if (parse_double(argv[3], &max)) {
        fprintf(stderr, "%s: Invalid maximum.\n", argv[3]);
        return EXIT_FAILURE;
    }

    if (min > max) {
        const double tmp = min;
        min = max;
        max = tmp;
    }

    /* ... */

    return EXIT_SUCCESS;
}

The setlocale(LC_ALL, ""); tells the C library to examine the current environment, and set up localization to match. This program only uses the LC_CTYPE class, to determine which characters are whitespaces (spaces or tabs). Nevertheless, it is a good practice to get into: if, at some point, you wish to support characters like ä and , you can switch to wide characters and I/O.

As a learner, you can omit parse_long() and parse_double(), and replace them in the if clauses with sscanf(), and ignore localization. It saves you a few lines,

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    double min, max;
    long   n;

    /* Require "command N min max" -- four parameters,
     * including the executable file name (argv[0]). */

    if (argc != 4 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
        fprintf(stderr, "       %s N min max\n", argv[0]);
        return EXIT_FAILURE;
    }

    if (sscanf(argv[1], " %ld", &n) != 1 || n < 1L) {
        fprintf(stderr, "%s: Invalid N.\n", argv[1]);
        return EXIT_FAILURE;
    }

    if (sscanf(argv[2], " %lf", &min) != 1) {
        fprintf(stderr, "%s: Invalid minimum.\n", argv[2]);
        return EXIT_FAILURE;
    }

    if (sscanf(argv[3], " %lf", &max) != 1) {
        fprintf(stderr, "%s: Invalid maximum.\n", argv[3]);
        return EXIT_FAILURE;
    }

    if (min > max) {
        const double tmp = min;
        min = max;
        max = tmp;
    }

    /* ... */

    return EXIT_SUCCESS;
}

but in my opinion, why learn a way that is not sufficient in practice? I personally know of cases where silly assumptions like "People's names only contain letters A to Z" took dozens of hours to work around (a 411 service on a computing cluster with users with non-English names). We live in a global world, and you English speakers better get in line already, and drop your silly assumptions.

It is not like people seem to be able to learn localization afterwards, either. Most "experienced C programmers" that I've encountered do not seem to know, nor care, about localization or character set issues at all. (Well, beyond using UTF-8 everywhere.) That means others have to spend hours and hours to work around their mis-assumptions, wasting time and effort... Disgraceful.


When you have your program in a form that will accept the parameters on the command line, you can use Bash loops like

for (( i=1; i<=20; i++ )); do ./yourprog $i 0.0 10.0 ; done > output.txt

Note that if you output data into space or tab-separated columns, with something like * or - if a column is missing data, you can use gnuplot to plot the data.

For example, if you have output.txt with

#N result error
1  3.1   0.04159265359
2  3.14  0.00159265359
3  3.141 0.00059265359

and so on, you can look at the data using e.g.

gnuplot -p -e 'plot "output.txt" u 2:3 notitle w lines'

Gnuplot ignores lines beginning with a #, so you can use such for comments or headers at the beginning of the file telling what each column is for. See the documentation for further information. I personally prefer to save plots in SVG or PDF format, so that they're small files, but with high quality vector graphics. This is what I recommend for coursework, in particular.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.