
私はシェル スクリプトの初心者です。 、 の C プログラムを呼び出し、後でこのデータをファイルに記録する Unix スクリプトを作成したいと考えてい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
私のgccのバージョンはgcc (Ubuntu 5.4.0-6ubuntu1~16.04.2) 5.4.0 20160609
インターネットでランダムに検索しましたが、役に立つ情報は見つかりませんでした。コードも変更する必要があると思います。Unix スクリプトの作成と、後で出力をファイルにリダイレクトするのを手伝っていただければ幸いです.txt
。行の説明が付いた Unix スクリプトは非常に役立ちます。
答え1
ユーザーにプロンプトを表示して標準入力からパラメータを読み取る代わりに、Unix哲学代わりにコマンドラインパラメータを使用してください。
次の例は、私が好むパラメータ チェック関数を示したかったため、少し長くなっています。scanf()
関数のファミリはオーバーフローをチェックしないため、strto*()
使用する必要があります。さらに、数字の後にゴミが付く場合があります (たとえば、'121' ではなく '12l' -- 最後が文字 L -- になる)。これは個人的にはキャッチしたいものです。
#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()
10進数(987
)、16進数(0x3DB
)、8進数(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
ように指示します。このプログラムは、クラス のみを使用して、どの文字が空白 (スペースまたはタブ) であるかを判断します。ただし、これは良い習慣です。ある時点で や などの文字をサポートしたい場合はä
、€
ワイド文字と I/O に切り替えることができます。
学習者としては、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 の後に追加し、各 3 つをプログラムの呼び出しにパイプすることができます。
for ((i=2;i<2**20;i*=2)); do echo "$i -1 1" | ./err ; done > out.txt
この > out.txt
部分は、すべての ./err 呼び出しの出力をすべて にリダイレクトしますout.txt
。