Escribir un script de shell Unix para llamar a una función C y redirigir datos a un archivo .txt

Escribir un script de shell Unix para llamar a una función C y redirigir datos a un archivo .txt

Soy nuevo en las secuencias de comandos de Shell. Deseo escribir un script Unix que llame a un programa C para N= 2^{i},; i= 1,2 ....20y posteriormente registrar estos datos en un archivo.

(Este programa calcula la integral definida usando la regla del trapezoide en C y devuelve resultados y errores iterativos para cada término N.)

Mientras comenzaba a aprender C, escribí mi código para la regla del trapezoide:

#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);

}

Ejecuto este código por

gcc -o err.o trap_error.c -lm

y mi versión de gcc esgcc (Ubuntu 5.4.0-6ubuntu1~16.04.2) 5.4.0 20160609

Busqué en Internet al azar, pero no encontré información útil, creo que también tengo que modificar mi código. Si me ayudas a escribir el script Unix y luego redirigir la salida en un .txtarchivo. Un script Unix con líneas explicadas será de gran ayuda.

Respuesta1

En lugar de preguntar a los usuarios y leer los parámetros de la entrada estándar, debería adoptar laFilosofía Unixy utilice parámetros de línea de comandos en su lugar.

El siguiente ejemplo es un poco largo porque quería mostrar mis funciones de verificación de parámetros preferidas. scanf()La familia de funciones no comprueba el desbordamiento, por lo que strto*()es necesario utilizarla. Además, ocasionalmente puede haber basura después del número (digamos, '12l' (la última es la letra L) en lugar de '121'), que personalmente quiero detectar.

#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;
}

De lo anterior, parse_long()admite notación decimal ( 987), hexadecimal ( 0x3DB) y octal ( 01733).

Entonces es main()algo así como

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;
}

Le setlocale(LC_ALL, "");dice a la biblioteca C que examine el entorno actual y configure la localización para que coincida. Este programa solo usa la LC_CTYPEclase para determinar qué caracteres son espacios en blanco (espacios o tabulaciones). Sin embargo, es una buena práctica: si, en algún momento, deseas admitir caracteres como äy , puedes cambiar a caracteres anchos y E/S.

Como alumno, puede omitir parse_long()y parse_double()reemplazarlos en las ifcláusulas por sscanf()e ignorar la localización. Te ahorra algunas líneas,

#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;
}

pero en mi opinión, ¿por qué aprender de una manera que no es suficiente en la práctica? Personalmente conozco casos en los que suposiciones tontas como"Los nombres de las personas sólo contienen letras de la A a la Z"Tomó decenas de horas solucionarlo (un servicio 411 en un grupo informático con usuarios con nombres que no estaban en inglés). Vivimos en un mundo global, y ustedes, los angloparlantes, será mejor que se pongan a la cola ya y abandonen sus tontas suposiciones.

Tampoco es que la gente parezca poder aprender la localización después. La mayoría de los "programadores experimentados en C" que he conocido no parecen saber ni preocuparse en absoluto por los problemas de localización o conjunto de caracteres. (Mucho más alláusando UTF-8 en todas partes.) Eso significa que otros tienen que pasar horas y horas para solucionar sus suposiciones erróneas, perdiendo tiempo y esfuerzo... Vergonzoso.


Cuando tenga su programa en un formato que acepte los parámetros en la línea de comando, puede usar bucles Bash como

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

Tenga en cuenta que si genera datos en espacios o columnas separadas por tabulaciones, con algo como *o -si a una columna le faltan datos, puede utilizarlos gnuplotpara trazar los datos.

Por ejemplo, si tienes output.txtcon

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

y así sucesivamente, puedes ver los datos usando, por ejemplo

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

Gnuplot ignora las líneas que comienzan con #, por lo que puede usarlas para comentarios o encabezados al comienzo del archivo que indiquen para qué sirve cada columna. Ver eldocumentaciónpara mayor información. Personalmente prefiero guardar los gráficos en formato SVGo PDF, para que sean archivos pequeños, pero con gráficos vectoriales de alta calidad. Esto es lo que recomiendo para los cursos, en particular.

Respuesta2

Estás compilando y vinculando, por lo que el resultado será un archivo ejecutable, no un archivo objeto, lo que hace que el sufijo .o sea engañoso.

gcc -o err trap_error.c -lm

podría ser una mejor idea.

No está muy claro lo que estás preguntando, pero parece que estás intentando proporcionarle alguna entrada generada automáticamente y redirigir todas las salidas a un archivo. Puedes generar N= 2^{i}, i= 1,2 ....20;en bash con:

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

Si -1 y 1 van a ser su límite inferior y superior respectivamente, entonces puede agregarlos después de $i y canalizar cada triplete a una invocación de su programa:

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

La > out.txtparte redirigirá todas las salidas de todas las invocaciones ./err a out.txt.

información relacionada