AnteriorPosterior

13. Servicios del sistema (a: fecha y hora, sistema de ficheros)

  Curso: Programación en Pascal (v5), por Nacho Cabanes

13. Servicios del sistema

En Turbo Pascal (y, por compatibilidad con él, también en Free Pascal y otros compiladores minoritarios como TMT Pascal), la unidad DOS nos permite acceder a muchod servicios del sistema operativo, como la fecha y hora, la lista de ficheros y también nos permite ejecutar otros programas. Vamos a ver ejemplos de esas posibilidades.

13.1. Fecha del sistema

Para saber la fecha actual, usaremos "GetDate", que nos devolverá el año, mes, día y número de día de la semana, en ese orden. Todos ellos son números enteros, y el día de la semana será 0 para el domingo, 1 para el lunes, 2 para el martes y así sucesivamente. Por tanto, un ejemplo que muestre la fecha del sistema, incluyendo el nombre del día, sería:


(* FECHA.PAS, Conocer la fecha actual   *)
(* Parte de CUPAS5, por Nacho Cabanes   *) 
 
program Fecha;
 
uses Dos;
 
const
    nombre : array [0..6] of String[15] =
        ('Domingo','Lunes','Martes','Miércoles',
         'Jueves','Viernes','Sábado');
var
    anyo, mes, dia, diaSem : Word;
 
begin
    GetDate( anyo, mes, dia, diaSem );
    writeln('Hoy es ', nombre[diaSem], ' ',
        dia, '/', mes, '/', anyo);
end.
 

De igual modo, tenemos "GetTime", que nos dice la hora, minutos, segundos y centésimas de segundo.

Ejercicio propuesto 13.1.1: Crea un programa que muestra la hora actual en la esquina superior izquierda de la pantalla, en formato HH:MM:SS. Debe actualizarse continuamente, hasta que se pulse una tecla.

13.2. Acceso al sistema de ficheros

Hemos visto la forma leer datos desde un fichero, pero no sabemos como ver qué ficheros hay en una carpeta, ni que unidades de disco hay en un equipo, ni el espacio libre. Todo ello puede resultar útil en programas un poco más avanzados.

13.2.1. Espacio libre en una unidad de disco

DiskFree nos devolverá el espacio libre en una unidad de disco y DiskSize nos dirá el tamaño total. En ambos casos, el dato que recibimos será la cantidad de bytes. Como parámetro debemos indicar que unidad queremos revisar : un 0 indicará que queremos saberlo para la unidad actual, un 1 se referiría a la A, un 2 a la B, un 3 a la C, y así sucesivamente. Si una unidad no existe, DiskSize y DiskFree devuelven -1.

(* ESPACIO.PAS, Espacio libre en la unid. de disco actual   *)
(* Parte de CUPAS5, por Nacho Cabanes                       *) 
 
program Espacio;  { Mira el espacio libre en la unidad de disco actual }
 
uses Dos;
begin
    write('Hay disponibles: ');
    write( DiskFree(0) div 1024 );        { Muestra el espacio libre }
    write(' K de ');
    write( DiskSize(0) div 1024 );        { y el espacio total }
    writeln(' K totales.');
end. 
 

Ejercicio propuesto 13.2.1.1: Muestra el espacio libre en todas las unidades de disco de tu sistema (sólo las que realmente existan, es decir, aquellas para las que DiskFree no valga -1).

13.2.2. Lista de ficheros

Podemos ver la lista de los ficheros que hay en un directorio mediante FindFirst y FindNext. Con FindFirst obtenemos el primer fichero que cumple una cierta condición, y con FindNext el siguiente (si existe). Podemos saber si hay "siguientes" mirando el valor de la variable DosError, que valdrá 0 mientras queden ficheros por analizar.

FindFirst recibe tres parámetros:

  • El nombre del fichero que buscamos, que normalmente tendrá "comodines", como el asterisco para indicar "cualquier nombre": "*.PAS" querría decir "fichero en Pascal con cualquier nombre".
  • Los atributos que debe tener ese fichero. En general será AnyFile, para indicar que nos importan todos los ficheros encontrados. Otros valores posibles son ReadOnly (sólo lectura), Hidden (oculto), SysFile (de sistema), Directory (directorio), VolumeID (etiqueta de volumen) y Archive (pendiente de archivar en una copia de seguridad).
  • La variable en la que se nos devolverán los datos del primer fichero encontrado. Es un record de un tipo llamado SearchRec, con campos como "Name" para el nombre del fichero, "Size" para su tamaño, "Time" para su fecha y hora y "Attr" para sus atributos (con los valores que acabamos de comentar).

FindNext recibe sólo un parámetro, la variable en la que se devolverán los datos del siguiente fichero.

Por ejemplo, para ver los ficheros que tienen extensión .PAS en la carpeta actual, podemos hacer:

(* LISTADIR.PAS, Lista de ficheros en un directorio *)
(* Parte de CUPAS5, por Nacho Cabanes               *) 
 
program ListaDir;
 
uses Dos;
 
var 
    Hallado: SearchRec;            { Información sobre el directorio }
                  { El tipo SearchRec está definido en la unidad Dos }
 
begin
    FindFirst( '*.PAS', AnyFile, Hallado );       { Los busca }
    while DosError = 0 do                        { Mientras existan }
        begin
        Writeln( Hallado.Name );      { Escribe el nombre hallado }
        FindNext( Hallado );          { y busca el siguiente }
        end;
end.
 

Ejercicio propuesto 13.2.2.1: Haz un programa que muestre la lista de ejecutables de la carpeta actual.
Ejercicio propuesto 13.2.2.2: Crea un programa que muestre la lista de subdirectorios de la carpeta actual.

13.2.3. Fecha y hora de ficheros

También podemos conocer la fecha y hora de creación de un fichero. Por una parte, hemos visto que es uno de los datos que podemos obtener con FindFirst y FindNext. Por otra parte, también podemos usar GetFtime para obtener directamente la información sobre un único fichero concreto. En ambos casos, la información de fecha y hora está "compactada" en un único byte, y debermos usar la orden "UnpackTime" para extraerla y volcarla a un dato de tipo "DateTime", como en este ejemplo:

(* FTIME.PAS, Ver fecha y hora de un fichero *)
(* Parte de CUPAS5, por Nacho Cabanes       *) 
 
uses Dos;
 
var
    fichero: text;
    h, m, s, cent: Word;
    fechaYhora: Longint;
    datos: DateTime;
 
begin
    Assign(fichero, 'FECHA.PAS');
    reset(fichero);
    GetFTime(fichero, fechaYhora);
    UnpackTime(fechaYhora, datos);
    WriteLn('Fecha del fichero: ',
        datos.day, '/',
        datos.month, '/',
        datos.year);    
    WriteLn('Hora del fichero: ',
        datos.hour, ':',
        datos.min, ':',
        datos.sec);
    close(fichero);
end.
 
 

Ejercicio propuesto 13.2.3.1: Crea un programa que pida al usuario el nombre de un fichero, y le diga la fecha y hora de ese fichero o un aviso de "El fichero no existe", según corresponda.

De forma alternativa, existe "SetFTime(fichero, fechaYhora)", que cambia la fecha y hora de un fichero, a partir de unos datos que se habrán compactado previamente con "PackTime(datos, fechaYhora)". Se puede ver que el formato de SetFTime es el mismo que el de GetFTime, pero el de "PackTime" es el contrario que el de "UnpackTime" (el dato en formato "DateTime" aparece en primer lugar, y la fecha y hora compactada en segundo lugar).

Ejercicio propuesto 13.2.3.2: Crea un programa que pida al usuario el nombre de un fichero y cambie su fecha y hora por la del instante actual.

13.3. Variables de entorno

Con GetEnv podemos saber el valor de una "variable de entorno" del sistema operativo. Por ejemplo, podemos saber el Path (ruta de búsqueda para ejecutables) con:

(* PATH.PAS, Descubrir el Path del sistema *)
(* Parte de CUPAS5, por Nacho Cabanes      *) 
 
program Path;
 
uses dos;
 
begin
    Write('El PATH es: ');
    WriteLn( GetEnv('PATH') );
end. 
 

Ejercicio propuesto 13.3.1: Crea un programa que muestre el nombre de la carpeta temporal del sistema, que suele estar guardada en la variable de entorno llamada "TMP".

13.4. Ejecutar otros programas

13.4.1. Ejecutar programas externos

También podemos lanzar otros programas desde uno nuestro. La forma básica es emplear la orden EXEC, a la que se le indica la ruta completa del fichero y los parámetros adicionales que queramos para ese fichero. Por ejemplo, bajo Windows podríamos lanzar el bloc de notas con C:\WINDOWS\NOTEPAD.EXE (suponiendo que Windows se ha instalado en la ruta habitual). Si además queremos que se abra directamente mostrando el contenido de un documento llamado "MITEXTO.TXT", lo indicaríamos como segundo parámetro de la orden EXEC).

Además, en el caso de Turbo Pascal para DOS, es necesario reservar memoria para ese programa que vamos a lanzar, porque si no lo pedimos de forma explícita, nuestro programa reservará para el mismo toda la memoria del sistema, al ser MsDos un sistema operativo monotarea. La forma de conseguirlo es con la directiva $M, a la que hay que indicar cuánta pila reservar (2000 puede ser suficiente para un programa sencillo y que no use recursividad) y cuanto "heap" mínimo y máximo queremos para variables dinámicas (ambos datos podrían ser 0 si nuestro programa no usa "punteros").

También (y, nuevamente, sólo en el caso de MsDos), debemos guardar los vectores de interrupción con SwapVectors antes de ejecutar nuestro programa y restaurarlos con la misma orden tras la ejecución.

(* EJECUTA.PAS, Ejecuta otro prog. desde el nuestro *)
(* Parte de CUPAS5, por Nacho Cabanes               *) 
 
{$M 2000,0,0 }   { 2000 bytes pila, sin heap }
{ Habrá que suprimir la línea anterior si se usa Tmt Pascal }
 
program EjecutaOrden;
 
uses dos;
 
begin
    SwapVectors;
    Exec('C:\WINDOWS\NOTEPAD.EXE', 'MITEXTO.TXT');
 
    (* En MsDOS puro, podría ser:
    Exec('C:\WP51\WP.EXE', 'MITEXTO.TXT');
    *)
    SwapVectors;
end. 
 

En el caso de FreePascal, podemos dejar la directiva $M, pero no se le hará caso; para TMT Pascal habría que eliminarla por completo.

13.4.2. Ejecutar órdenes del sistema operativo

Podemos ejecutar ordenes del DOS como DIR y otros programas cuyo nombre completo (incluyendo directorios) no conozcamos, pero para eso hemos de llamar a COMMAND.COM con la opción /C:

(* EJECUTA2.PAS, Ejecuta una orden interna de MsDos    *)
(* No funcionará en la mayoría de versiones de Windows *)
(* Parte de CUPAS5, por Nacho Cabanes                  *) 
 
{$M 2000,0,0 }   { 2000 bytes pila, sin heap }
{ Habrá que suprimir la línea anterior si se usa Tmt Pascal }
 
program EjecutaOrden2;
 
uses dos;
 
begin
    SwapVectors;
    Exec('C:\COMMAND.COM', '/C DIR *.PAS');
    SwapVectors;
end. 
 

Pero puede ocurrir si nuestro intérprete de comandos no sea COMMAND.COM. Esto pasará en versiones recientes de Windos, que usen CMD.EXE, y también en ciertos casos bajo MsDos, si se emplean intérpretes de comandos alternativos, como 4DOS o NDOS. Entonces podemos recurrir a la variable de entorno COMSPEC, que nos dice cual es el intérprete de comandos que estamos usando. Así, nuestra orden Exec quedaría:

Exec( GetEnv('COMSPEC'), '/C DIR *.PAS');

Y el fuente completo sería:

(* EJECUTA3.PAS, Ejecuta una orden interna de MsDos/Win *)
(* Parte de CUPAS5, por Nacho Cabanes                   *) 
 
{$M 2000,0,0 }   { 2000 bytes pila, sin heap }
{ Habrá que suprimir la línea anterior si se usa Tmt Pascal }
 
program EjecutaOrden3;
 
uses dos;
 
begin
    SwapVectors;
    Exec( GetEnv('COMSPEC'), '/C DIR *.PAS');
    SwapVectors;
end. 
 

Ejercicio propuesto 13.4.1: Crea un programa que permita acceder pulsando una sola tecla a algunas aplicaciones que tengas en tu sistema (por ejemplo, en Windows tendrás el bloc de notas como NOTEPAD.EXE, la calculadora como CALC.EXE y WordPad como WRITE.EXE, todos ellos en la carpeta de Windows).

Actualizado el: 07-08-2014 11:53

AnteriorPosterior