
Eu sou novo em scripts Shell. Desejo escrever um script Unix que chamará um programa C para N= 2^{i}
, i= 1,2 ....20
; e posteriormente registrar esses dados em um arquivo.
(Este programa calcula integral definida usando a regra do trapézio em C e retorna resultado iterativo e erro para cada termo N.)
Enquanto comecei a aprender C, escrevi meu código para a regra do trapézio:
#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);
}
Eu executo esse código por
gcc -o err.o trap_error.c -lm
e minha versão do gcc égcc (Ubuntu 5.4.0-6ubuntu1~16.04.2) 5.4.0 20160609
Pesquisei na Internet aleatoriamente, mas não encontrei informações úteis, acho que também preciso modificar meu código. Se você apenas me ajudar a escrever o script Unix e depois redirecionar a saída em um .txt
arquivo. Um script Unix com linhas explicadas será muito útil.
Responder1
Em vez de solicitar aos usuários e ler os parâmetros da entrada padrão, você deve adotar oFilosofia Unixe use parâmetros de linha de comando.
O exemplo a seguir é um pouco longo, porque eu queria mostrar minhas funções preferidas de verificação de parâmetros. scanf()
família de funções não verifica estouro, então strto*()
precisa ser usada. Além disso, ocasionalmente pode haver lixo após o número (digamos, '12l' - sendo a última letra L - em vez de '121'), que eu pessoalmente quero capturar.
#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;
}
Acima, parse_long()
suporta notação decimal ( 987
), hexadecimal ( 0x3DB
) e octal ( 01733
).
O main()
é então algo 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;
}
Diz setlocale(LC_ALL, "");
à biblioteca C para examinar o ambiente atual e configurar a localização para corresponder. Este programa usa apenas a LC_CTYPE
classe para determinar quais caracteres são espaços em branco (espaços ou tabulações). No entanto, é uma boa prática entrar: se, em algum momento, você desejar oferecer suporte a caracteres como ä
e €
, poderá mudar para caracteres largos e E/S.
Como aluno, você pode omitir parse_long()
e parse_double()
substituí-los nas if
cláusulas por sscanf()
e ignorar a localização. Isso economiza algumas linhas,
#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;
}
mas na minha opinião, por que aprender uma forma que não é suficiente na prática? Eu pessoalmente conheço casos em que suposições tolas como"Os nomes das pessoas contêm apenas letras de A a Z"levou dezenas de horas para ser solucionado (um serviço 411 em um cluster de computação com usuários com nomes que não falam inglês). Vivemos em um mundo global, e é melhor vocês, falantes de inglês, entrarem na fila e abandonarem suas suposições tolas.
Também não parece que as pessoas consigam aprender a localização depois. A maioria dos "programadores C experientes" que encontrei não parece saber nem se importar com problemas de localização ou conjunto de caracteres. (Bem alémusando UTF-8 em qualquer lugar.) Isso significa que outros têm que gastar horas e horas para contornar suas suposições erradas, desperdiçando tempo e esforço... Vergonhoso.
Quando você tem seu programa em um formato que aceita os parâmetros na linha de comando, você pode usar loops Bash como
for (( i=1; i<=20; i++ )); do ./yourprog $i 0.0 10.0 ; done > output.txt
Observe que se você gerar dados em colunas separadas por espaço ou por tabulação, com algo como *
ou -
se uma coluna estiver faltando dados, você poderá usar gnuplot
para plotar os dados.
Por exemplo, se você tiver output.txt
com
#N result error
1 3.1 0.04159265359
2 3.14 0.00159265359
3 3.141 0.00059265359
e assim por diante, você pode ver os dados usando, por exemplo
gnuplot -p -e 'plot "output.txt" u 2:3 notitle w lines'
O Gnuplot ignora linhas que começam com #
, então você pode usá-las para comentários ou cabeçalhos no início do arquivo, informando para que serve cada coluna. Veja odocumentaçãopara mais informações. Pessoalmente, prefiro salvar os gráficos em formato SVG
ou PDF
formato, para que sejam arquivos pequenos, mas com gráficos vetoriais de alta qualidade. Isso é o que recomendo para cursos, em particular.
Responder2
Você está compilando e vinculando, portanto a saída será um arquivo executável, não um arquivo objeto, o que torna o sufixo .o enganoso.
gcc -o err trap_error.c -lm
pode ser uma ideia melhor.
Não está muito claro o que você está perguntando, mas parece que você está tentando alimentá-lo com alguma entrada gerada automaticamente e redirecionar todas as saídas para um arquivo. Você pode gerar N= 2^{i}, i= 1,2 ....20;
no bash com:
for ((i=2;i<2**20;i*=2)); do echo "$i" ; done
Se -1 e 1 forem seus limites inferior e superior, respectivamente, você poderá adicioná-los depois de $i e canalizar cada triplo para uma invocação do seu programa:
for ((i=2;i<2**20;i*=2)); do echo "$i -1 1" | ./err ; done > out.txt
A > out.txt
parte redirecionará todas as saídas de todas as invocações ./err para out.txt
.