
Ich bin neu im Shell-Scripting. Ich möchte ein Unix-Skript schreiben, das ein C-Programm für N= 2^{i}
, i= 1,2 ....20
; aufruft und diese Daten später in einer Datei aufzeichnet.
(Dieses Programm berechnet das bestimmte Integral mithilfe der Trapezregel in C und gibt das iterative Ergebnis und den Fehler für jeden Term N zurück.)
Als ich gerade angefangen habe, C zu lernen, habe ich meinen Code für die Trapezregel geschrieben:
#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);
}
Ich führe diesen Code aus, indem ich
gcc -o err.o trap_error.c -lm
und meine gcc-Version istgcc (Ubuntu 5.4.0-6ubuntu1~16.04.2) 5.4.0 20160609
Ich habe im Internet wahllos gesucht, aber keine nützlichen Informationen gefunden. Ich glaube, ich muss auch meinen Code ändern. Wenn Sie mir einfach helfen, das Unix-Skript zu schreiben und die Ausgabe später in eine .txt
Datei umzuleiten. Ein Unix-Skript mit erklärten Zeilen wäre sehr hilfreich.
Antwort1
Anstatt Benutzer aufzufordern und die Parameter von der Standardeingabe zu lesen, sollten Sie dieUnix-Philosophie, und verwenden Sie stattdessen Befehlszeilenparameter.
Das folgende Beispiel ist etwas lang, weil ich meine bevorzugten Parameterprüffunktionen zeigen wollte. scanf()
Die Funktionsfamilie prüft nicht auf Überlauf und strto*()
muss daher verwendet werden. Außerdem kann es gelegentlich zu Datenmüll nach der Zahl kommen (z. B. „12l“ – zuletzt der Buchstabe L – statt „121“), den ich persönlich abfangen möchte.
#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;
}
Von den oben genannten parse_long()
werden die dezimale ( 987
), hexadezimale ( 0x3DB
) und oktale ( 01733
) Notation unterstützt.
Das main()
ist dann sowas wie
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;
}
Das setlocale(LC_ALL, "");
weist die C-Bibliothek an, die aktuelle Umgebung zu untersuchen und die Lokalisierung entsprechend einzurichten. Dieses Programm verwendet die LC_CTYPE
Klasse nur, um zu bestimmen, welche Zeichen Leerzeichen (Leerzeichen oder Tabulatoren) sind. Trotzdem ist es eine gute Praxis, sich damit vertraut zu machen: Wenn Sie irgendwann Zeichen wie ä
und unterstützen möchten €
, können Sie auf breite Zeichen und E/A umsteigen.
Als Lernender können Sie parse_long()
und weglassen parse_double()
und in den if
Klauseln durch ersetzen sscanf()
und die Lokalisierung ignorieren. Das spart Ihnen ein paar Zeilen,
#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;
}
aber meiner Meinung nach, warum sollte man einen Weg lernen, der in der Praxis nicht ausreicht? Ich persönlich kenne Fälle, in denen dumme Annahmen wie„Menschennamen bestehen nur aus den Buchstaben A bis Z“es hat Dutzende von Stunden gedauert, das Problem zu umgehen (ein 411-Dienst auf einem Computercluster mit Benutzern mit nicht-englischen Namen). Wir leben in einer globalen Welt, und Sie Englischsprachigen sollten sich besser schon mal einreihen und Ihre dummen Annahmen fallen lassen.
Es ist auch nicht so, dass die Leute im Nachhinein Lokalisierung lernen könnten. Die meisten „erfahrenen C-Programmierer“, die ich kennengelernt habe, scheinen sich überhaupt nicht mit Lokalisierungs- oder Zeichensatzproblemen auszukennen oder sich darum zu kümmern. (Nun, abgesehen vonüberall UTF-8 verwenden.) Das bedeutet, dass andere stundenlang versuchen müssen, ihre falschen Annahmen zu umgehen, und dabei Zeit und Mühe verschwenden … Eine Schande.
Wenn Ihr Programm eine Form hat, die Parameter in der Befehlszeile akzeptiert, können Sie Bash-Schleifen wie folgt verwenden:
for (( i=1; i<=20; i++ )); do ./yourprog $i 0.0 10.0 ; done > output.txt
Beachten Sie, dass Sie zum Plotten der Daten verwenden können , wenn Sie Daten in durch Leerzeichen oder Tabulatoren getrennte Spalten ausgeben, beispielsweise mit *
oder -
wenn in einer Spalte Daten fehlen .gnuplot
Wenn Sie beispielsweise output.txt
mit
#N result error
1 3.1 0.04159265359
2 3.14 0.00159265359
3 3.141 0.00059265359
und so weiter, Sie können die Daten beispielsweise betrachten mit
gnuplot -p -e 'plot "output.txt" u 2:3 notitle w lines'
Gnuplot ignoriert Zeilen, die mit einem beginnen #
, daher können Sie solche für Kommentare oder Überschriften am Anfang der Datei verwenden, die angeben, wofür jede Spalte ist. Siehe dieDokumentationSVG
für weitere Informationen. Ich persönlich bevorzuge es, Diagramme im - oder -Format zu speichern PDF
, sodass es sich um kleine Dateien mit hochwertigen Vektorgrafiken handelt. Dies empfehle ich insbesondere für Kursarbeiten.
Antwort2
Sie kompilieren und verknüpfen, daher ist die Ausgabe eine ausführbare Datei und keine Objektdatei, weshalb die Endung .o irreführend ist.
gcc -o err trap_error.c -lm
könnte eine bessere Idee sein.
Es ist nicht ganz klar, was Sie fragen, aber es sieht so aus, als würden Sie versuchen, automatisch generierte Eingaben zu übermitteln und alle Ausgaben in eine Datei umzuleiten. Sie können N= 2^{i}, i= 1,2 ....20;
in Bash Folgendes generieren:
for ((i=2;i<2**20;i*=2)); do echo "$i" ; done
Wenn -1 und 1 Ihre Unter- bzw. Obergrenze sein sollen, können Sie diese nach $i hinzufügen und jedes dieser Tripel an einen Aufruf Ihres Programms weiterleiten:
for ((i=2;i<2**20;i*=2)); do echo "$i -1 1" | ./err ; done > out.txt
Der > out.txt
Teil leitet alle Ausgaben aller ./err-Aufrufe in um out.txt
.