Написание скрипта оболочки Unix для вызова функции C и перенаправления данных в файл .txt

Написание скрипта оболочки Unix для вызова функции C и перенаправления данных в файл .txt

Я новичок в написании скриптов Shell. Я хочу написать скрипт Unix, который будет вызывать программу C для N= 2^{i}, i= 1,2 ....20; и позже записывать эти данные в файл.

(Эта программа вычисляет определенный интеграл с использованием формулы трапеций в языке C и возвращает итерационный результат и ошибку для каждого члена N.)

Когда я только начал изучать C, я написал свой код для правила трапеции:

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

}

Я выполняю этот код

gcc -o err.o trap_error.c -lm

и моя версия gccgcc (Ubuntu 5.4.0-6ubuntu1~16.04.2) 5.4.0 20160609

Я искал в Интернете наугад, но не нашел полезной информации, думаю, мне также придется изменить свой код. Если вы просто поможете мне написать скрипт Unix и позже перенаправить вывод в файл .txt. Скрипт Unix с объясненными строками будет очень полезен.

решение1

Вместо того, чтобы запрашивать у пользователей и считывать параметры из стандартного ввода, вам следует использоватьФилософия Unixи вместо этого используйте параметры командной строки.

Следующий пример немного длинный, потому что я хотел показать свои любимые функции проверки параметров. scanf()Семейство функций не проверяет переполнение, поэтому его strto*()нужно использовать. Кроме того, после числа иногда может быть мусор (например, '12l' — последняя буква L — вместо '121'), который я лично хочу отловить.

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

Из вышеперечисленного parse_long()поддерживает десятичную ( 987), шестнадцатеричную ( 0x3DB) и восьмеричную ( 01733) системы счисления.

main()Тогда это что-то вроде

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

Сообщает setlocale(LC_ALL, "");библиотеке C о необходимости проверки текущей среды и настройки локализации для соответствия. Эта программа использует класс только LC_CTYPEдля определения того, какие символы являются пробелами (пробелами или табуляциями). Тем не менее, это хорошая практика: если в какой-то момент вы захотите поддерживать такие символы, как äи , вы можете переключиться на широкие символы и ввод-вывод.

Как учащийся, вы можете опустить parse_long()и parse_double(), и заменить их в ifпредложениях на sscanf(), и игнорировать локализацию. Это сэкономит вам несколько строк,

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

но по-моему, зачем изучать способ, который недостаточен на практике? Я лично знаю случаи, когда глупые предположения типа«Имена людей содержат только буквы от A до Z»Потребовалось несколько десятков часов, чтобы обойти эту проблему (служба 411 на вычислительном кластере с пользователями с неанглийскими именами). Мы живем в глобальном мире, и вам, носителям английского языка, лучше бы уже встать в очередь и отказаться от своих глупых предположений.

Не похоже, что люди способны изучить локализацию впоследствии. Большинство "опытных программистов на C", с которыми я сталкивался, похоже, вообще не знают и не заботятся о проблемах локализации или набора символов. (Ну, кромевезде используем UTF-8.) Это означает, что другим приходится тратить часы и часы на то, чтобы обойти свои ошибочные предположения, теряя время и силы... Позор.


Когда ваша программа будет принимать параметры в командной строке, вы можете использовать циклы Bash, например:

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

Обратите внимание, что если вы выводите данные в столбцы, разделенные пробелами или табуляцией, например, *или -если в столбце отсутствуют данные, вы можете использовать gnuplotдля построения графика данных.

Например, если у вас есть output.txtс

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

и так далее, вы можете просмотреть данные, используя, например,

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

Gnuplot игнорирует строки, начинающиеся с #, поэтому вы можете использовать их для комментариев или заголовков в начале файла, сообщающих, для чего предназначен каждый столбец. См.документациядля получения дополнительной информации. Лично я предпочитаю сохранять графики в формате SVGили PDF, чтобы они были небольшими файлами, но с высококачественной векторной графикой. Это то, что я рекомендую для курсовых работ, в частности.

решение2

Вы компилируете и компоновываете, поэтому на выходе получится исполняемый, а не объектный файл, поэтому суффикс .o вводит в заблуждение.

gcc -o err trap_error.c -lm

может быть лучшей идеей.

Не совсем понятно, о чем вы спрашиваете, но похоже, что вы пытаетесь скормить ему какой-то автоматически сгенерированный ввод и перенаправить все выводы в файл. Вы можете сгенерировать N= 2^{i}, i= 1,2 ....20;в bash с помощью:

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

Если -1 и 1 будут вашим нижним и верхним пределом соответственно, то вы можете добавить их после $i и передать каждую такую ​​тройку в вызов вашей программы:

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

Часть > out.txtперенаправит все выходные данные всех вызовов ./err в out.txt.

Связанный контент