
Я новичок в написании скриптов 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
.