Problemas con lenguaje c y redirecciones bajo linux

Buenas estoy haciendo una practiquilla pero estoy teniendo problemas con el tema de las redireciones. He creado un pequeño programa para ver si veis si estoy haciendo algo mal. Aqui va:

#include
#include
#include
int main()
{
int descfich;
printf("Ejemplo de redireccion");
descfich=open("f1.txt",O_CREAT,0666);
dup2(descfich,STDOUT_FILENO);

execvp("/usr/bin/free",NULL);
printf("HOLA MUNDO\n");
close(descfich);
return 0;
}


No estoy pidiendo que me lo hagais, ni nada, simplemente porque no funciona. El fichero lo crea, pero está vacio. He estado buscando informacion por internet pero los programas de prueba, tampoco me funcionan alguien sabe porque?
Hace mucho que no programo en C y tampoco es que supiera mucho, pero yo no veo que escrivas en el fichero
mmm no se trata de escribir en el fichero, es hacer que la salida estandar esté redirigida a un fichero, esto se logra haciendo movidillas con la tabla de descripcion de ficheros, manejando los manejadores con dup2 y close. asi que printf deberia dirigirse al fichero.
ah, pues tan lejos ya no llego, lo siento. Por si te puede servir de ayuda, no se si sabias que puedes hacer por ejemplo un man printf para que te enseñe una pagina de manual sobre printf ^__^
execvp("/usr/bin/free",NULL);
printf("HOLA MUNDO\n");
close(descfich);

Exec sustituy el proceso.
Las lineas que van despues no se ejecutaran nunca.

Sacado del man
DESCRIPCION
La familia de funciones exec reemplaza la imagen del proceso en curso
con una nueva. Las funciones descritas en esta pagina del Manual son
interfaces para la primitiva execve(2). (Consulte la pagina del Manual
de execve para informacion detallada acerca del reemplazo del proceso
en curso.)


Para ejecutar otro programa y seguir adelante primero tienes que usar
fork

fork parte el proceso en 2 en el padre devuelve el pid del hijo
y en el hijo 0

pid=fork();

if (pid==0){
   printf ("Soy el hijo");
}else{
    printf ("Soy el padre");
     if (pid<0)
        printf ("Ha habido un error creando el hijo");
}



Ahora ojo por que el hijo se ejecuta independientemente y para
esperar a que acabe necistas usar waitpid

pid_t waitpid(pid_t pid, int *status, int options);
Para redigir la salida estandar (es decir el canal 1 de la TFO) a la del fichero, antes del printf("hola mundo") en vez de usar el dup2 (que no lo he usado en mi corta vida), yo haria:

close(1);
dup(descfich);

De estas manera, primero cierras la salida estandar, i luego duplicas en la TFO la entrada del fichero, la duplicacion se coloca en la primera entrada libre, es decir en la 1, que la acabas de cerrar.

Otra cosa, seguro que ese execv, lo necessitats. Porque tal como dice harl sustituyes el proceso y se renueva la TFO, asi que todos los ficheros abiertos i redirecciones las pierdes.

Espero haverme explicado con claridad

Salu2
Bueno si, lo de que el exec sustituiria el proceso ya lo sabia, pero lo que me preocupa realmente es que ni el exec se ejecuta, osea, se redirige al archivo. la porcion de codigo es menor, y casualmente el codigo "de verdad" tambien hace uso de forks y estas cosas. Mi duda es PQ NO SE REDIRIGE, si he cambiado la tabla de manejadores :s


EDITADO: No habia leido este ultimo mensaje, voy a probarlo
UFFFFFFF acabo de hacer unos pequeños cambios, pero no hay forma. de momento no hago un exec, simplemente escribo con el printf, el codigo quedaria asi mas o menos:

int main()
{
int descfich;
printf("Ejemplo de redireccion");
descfich=open("f1.txt",O_CREAT,0666);
close(STDOUT_FILENO); //cierro la salida standard
dup(descfich); //duplico el fichero a la primera entrada libre, la que deje antes
printf("HOLA MUNDO\n");
dup2(STDOUT_FILENO,descfich); //para volver a dejar todo como deberia
close(descfich); //cierro el ficherito
return 0;
}
Bueno lo del exec creo que es por que la linea de parametros no puede
ser null (el primer argumento siempre es el nombre del ejecutable)

Te dejo como ejemplo una practica que hice hace .... o más
Es parte de una shell ejecucta dos programas unido por un pipe
(el tipico comando1 | comando2)
los parametros de entrada son la lista de argumentos
es un array de punteros a cadena donde el último puntero es null
y el resto apuntan a los distintos argumentos ej:

puntero1-> nombre_ejecutable
puntero2 -> parametro1
null (para indicar el fin)

int tuberia(char **arg1,char **arg2)
{
  int estatus1,estatus2;
  pid_t pid1,pid2;
  int pipes[2];

  if(pipe (pipes))
    {
      printf ("No se puede hacer la tuberia. 1\n");
      return(-1);
    }

  pid1=fork ();
  if (pid1 < 0)
    {
      printf ("Pipe: No se puede ejecutar el proceso.\n");
      exit (1);
    }
  if (pid1==0)
    {
      if(close(1))
   {
     printf ("No se puede hacer la tuberia. 2\n");
     return (-1);
   }
      if(close(pipes[0]))
   {
     printf ("No se puede hacer la tuberia. 3\n");
     return (-1);
   }
      if(-1==(dup(pipes[1])))
   {
     printf ("No se puede hacer la tuberia. 4\n");
     return (-1);
   }
      execvp (arg1[0],arg1);
      printf ("Pipe :%s comando no encontrado.\n",arg1[0]);
      exit (1);
    }

  pid2=fork ();
  if (pid2 < 0)
    {
      printf ("Pipe: No se puede ejecutar el proceso.\n");
      exit (1);
    }
  if (pid2!=0)
    {
      if(close(0))
   {
     printf ("No se puede hacer la tuberia. 5\n");
     return (-1);
   }
      if(close(pipes[1]))
   {
     printf ("No se puede hacer la tuberia. 6\n");
     return (-1);
   }
      if(-1==(dup(pipes[0])))
   {
     printf ("No se puede hacer la tuberia. 7\n");
     return (-1);
   }
      execvp (arg2[0],arg2);
      printf ("Pipe :%s comando no encontrado.\n",arg2[0]);
      exit (1);
    }

  waitpid(pid1,&estatus1,WUNTRACED);
  waitpid(pid2,&estatus2,WUNTRACED);
  return (estatus1*estatus2);
}
Dejaron unas pequeñas explicaciones con la creación de procesos y demás:

http://petra.euitio.uniovi.es/asignaturas/sis.ope/material/practicas/wys.pdf


Las redirecciones en Unix son un pelín complicadas, pero son relativamente lógicas.

Tienes una tabla de descriptores de ficheros, y las tres primeras entradas son la entrada estándar (0,stdin), la salida estándar (1,stdout), y la salida de errores estándar (2,stderr).

Lo que debes hacer es modificar la tabla, para que en vez de apuntar a lo que suelen apuntar (teclado la primera, y consola las otras dos), que apunten a un fichero. Para ello, en el caso de las salidas debes crear un fichero como escritura, y en la entrada abrirlo como lectura. Una vez hecho esto, lo más sencillo, es cerrar el descriptor de fichero que quieras modificar (0, 1 ó 2), y duplicar el descriptor del fichero al que redirijas. Al duplicarlo, se buscará la entrada de la tabla que se encuentre vacía. Como acabas de cerrar uno de los primeros, se duplicará allí. Así, cada vez que se haga una lectura/escritura en ese descriptor, se hará en el fichero. Y entonces puedes cerrar el descriptor del fichero creado, no lo necesitas más.

Esto tiene un problema, y es que haciéndolo así, no tienes forma de volver a la redirección anterior (a la consola), así que antes de hacerla, debes de guardar los descriptores originales. La manera de hacerlo, es duplicarlos a ellos también, y apuntar en que posición de la tabla se han guardado. Cuando ya no necesites la redirección, basta con volver a hacer la redirección, utilizando esos fd's.

Para crear el fichero o abrirlo, yo uso siempre open(), pero con diferentes flags, O_RDONLY para lectura, y O_WRONLY|O_CREAT|O_TRUNC para la escritura (sólo escritura, crear el fichero si no existe, y truncarlo si existe).

Para duplicar una entrada de la FDT, se usa dup(), se le pasa la posición de la tabla a duplicar, y devuelve la nueva posición de la copia.

Y para cerrar un FD, con close().

Una vez que se tenga esto, es hacer lo que dije antes:

-Abrimos el fichero a donde se hará la redirección.
-Duplicaremos el FD que queremos redireccionar (yo uso las macros STDOUT_FILENO, etc, no uso los números directamente), y guardamos la posición de la copia
-Cerramos el descriptor original
-Duplicamos el descriptor del fichero abierto, creándose la copia donde estaba la estándar (no es necesario guardar donde se copia ;))
-Cerramos el descriptor del fichero (el que nos devolvió open)

Para resdirigir:

-Cerramos descriptor
-Duplicamos el original (que teníamos guardado)
-Cerramos la copia que teníamos.

Y ya está :-)
lo primero muchas gracias a todos, ahora ya se como funciona esto mas o menos bien (desgraciadamente me perdi esa clase). He estado probando y sigue sin funcionar, de momento no me preocupa que luego no pueda recuperar la salida standard, de momento lo que me preocupa esq nunca se me escribe el fichero. finalmente el código ha quedado así:

int nuevo,viejo;
printf("Ejemplo de redireccion");
nuevo=open("f3",O_CREAT|O_TRUNC,0666);
viejo=dup(STDOUT_FILENO);
close(STDOUT_FILENO);
dup(nuevo);
close(nuevo);
printf("NO FUNA NI DE COÑA!\n");


lo peor de todo es que creo haber seguido bien las indicaciones, y si simulo el proceso en un papel, deberia funcionar.
Tienes que especificarle a open(), el modo de apertura (lectura, escritura o las dos). Están las flags O_RDONLY, O_WRONLY u O_RDWR para ello. Todo está en el man ;)

Para ver que errores puede dar una función, tienes perror(), que muestra por pantalla una descripción del error almacenado en nerr. Verás que open no ha abierto ningún fichero, y te ha devuelto -1.
11 respuestas