Appunti per Scuola e Università
humanisticheUmanistiche
Appunti e tesine di tutte le materie per gli studenti delle scuole medie riguardanti le materie umanistiche: dall'italiano alla storia riguardanti le materie umanistiche: dall'italiano alla storia 
sceintificheScientifiche
Appunti, analisi, compresione per le scuole medie suddivisi per materie scientifiche, per ognuna troverai appunti, dispense, esercitazioni, tesi e riassunti in download.
tecnicheTecniche
Gli appunti, le tesine e riassunti di tecnica amministrativa, ingegneria tecnico, costruzione. Tutti gli appunti di AppuntiMania.com gratis!
Appunti
informatica
CComputerDatabaseInternetJava
Linux unixReti


AppuntiMania.com » Informatica » Appunti di c » Lavorare con i file batch

Lavorare con i file batch




Visite: 3247Gradito:apreciate 5-stela [ Grande appunti ]
Leggi anche appunti:

Vettori di interrupt o puntatori?


Vettori di interrupt o puntatori? Degli interrupt e dei loro vettori si parla

Allocazione dinamica della memoria


Allocazione dinamica della memoria Quando è dichiarata una variabile, il compilatore

Gli interrupt: utilizzo


Gli interrupt: utilizzo Gli interrupt sono routine, normalmente operanti a livello
immagine di categoria

Scarica gratis Lavorare con i file batch

Lavorare con i file batch

L'interprete dei comandi (COMMAND.COM nella configurazione DOS standard) fornisce una interfaccia per l'esecuzione dei programmi (a volte definiti comandi esterni ) e rende disponibili alcuni comandi interni, così detti in quanto implementati mediante routine interne all'interprete stesso (COPY DEL DIR, etc.), nonché la capacità, utilizzabile dalle applicazioni opportunamente progettate, di effettuare redirezioni (vedere pag. 

L'esecuzione di sequenze di comandi interni ed esterni può essere automatizzata mediante i file batch, che l'interprete è in grado di leggere ed eseguire riga per riga: ognuna contiene, in formato ASCII, un singolo comando; sono inoltre disponibili istruzioni per il controllo del flusso elaborativo, quali FOR IF (utilizzabili direttamente da prompt) e GOTO. Per i dettagli circa la sintassi dei comandi interni si rimanda alla manualistica DOS; in questa sede si vuole sottolineare che, per mezzo di questi soltanto, è spesso difficile (se non impossibile) implementare algoritmi di una certa complessità. Ad esempio, la IF consente di controllare il contenuto del registro ERRORLEVEL (vedere pag.  ) o di verificare l'esistenza di un file

IF EXIST PIPPO GOTO TROVATO

ECHO PIPPO NON C'E'

GOTO END

:TROVATO


:END

Non è tuttavia possibile effettuare test su alcuna caratteristica del file stesso, quali data, ora, dimensione, né accertare se si tratti piuttosto di una directory.

L'elenco dei principali limiti all'efficacia dei file batch può continuare a lungo: non vi è modo di inserire in comandi batch stringhe contenenti data e ora di elaborazione, o parti di esse; non è possibile ritardare l'esecuzione di un comando ad un'ora specificata; il comando COPY fallisce se il file origine ha dimensione 0 byte; non vi sono strumenti in grado di estrarre da un flusso ASCII parti di testo in modo 'mirato', ad eccezione del programma FIND, che ne visualizza le righe contenenti (o no) una data stringa; l'output di un comando non può essere utilizzato come parte di un successivo comando; una modifica alla lista degli argomenti del comando FOR richiede che sia modificato il file batch contenente il comando stesso

Dette limitazioni sono superate con una minima interattività da parte dell'utilizzatore, ma possono originare problemi quasi insormontabili laddove vi sia la necessità di una completa automazione (ad esempio in elaborazioni notturne): il presente paragrafo presenta alcuni programmi volti a superare le carenze cui si è fatto cenno . Lo scopo è fornire un insieme di spunti e idee perfettibili e, al tempo stesso, adattabili a piacere secondo le specifiche esigenze di ciascuno.

L'idea più semplice: EMPTYLVL

La semplicità del listato che segue è assolutamente disarmante: il programma legge lo standard input ed immediatamente termina, valorizzando il registro ERRORLEVEL se lo stream stdin è vuoto o contiene solo spazi, tabulazioni o caratteri di ritorno a capo; in caso contrario ERRORLEVEL è azzerato.



EMPTYLVL.C - 20/01/93 - Barninga_Z!


Legge una stringa da STDIN e setta il registro errorlevel a 1 se la

stringa contiene solo spazi e/o tabs e/o CR e/o LF. In caso contrario

il registro e' posto a 0.


Compilato sotto Borland C++ 3.01:


bcc -O -rd emptylvl.c




#include <stdio.h>

#include <string.h>


#define  MAXHEAP 4096

#define  MAXBUF 2048

#define  NULLCHARS ' tnr'


extern unsigned _heaplen = MAXHEAP;


int main(void)


Come si vede, l'unica particolarità tecnica di qualche rilevanza è costituita dall'assegnazione di un valore predefinito alla variabile _heaplen, per fissare la massima dimensione dello heap (vedere pag.  ) e limitare così al minimo indispensabile la quantità di memoria necessaria per il caricamento e l'esecuzione del programma.

EMPTYLVL si rivela utile in molte situazioni: ad esempio ci consente di scoprire se un file è vuoto, o se si tratta di una directory. Vediamo un esempio:

IF EXIST PIPPO GOTO TROVATO

ECHO PIPPO NON C'E'

GOTO END

:TROVATO

DIR | FIND 'PIPPO' | FIND '<DIR>' | EMPTYLVL

IF ERRORLEVEL 1 GOTO PIPPOFIL

ECHO PIPPO E' UNA DIRECTORY

GOTO END

:PIPPOFIL

DIR | FIND 'PIPPO' | FIND ' 0 ' | EMPTYLVL

IF ERRORLEVEL 1 GOTO PIPPODAT

ECHO IL FILE PIPPO E' VUOTO

GOTO END

:PIPPODAT

TYPE PIPPO | MORE

:END

Le prime righe del batch non rappresentano una novità rispetto quanto sopra accennato. Al contrario, la riga

DIR | FIND 'PIPPO' | FIND '<DIR>' | EMPTYLVL

merita qualche commento. Il comando DIR produce l'elenco di tutti i file e subdirectory presenti nella directory di default: il simbolo ' ' ne effettua il piping al primo comando FIND (esterno), che lo scandisce e scrive, a sua volta, sullo standard output solo le righe contenenti la stringa 'PIPPO'. Ma l'output del primo FIND è trasformato (ancora mediante piping) in standard input per il secondo comando FIND, che scrive sul proprio standard output solo le righe contenenti la stringa '<DIR>': ne segue che se il file PIPPO è, in realtà, una directory, lo standard input di EMPTYLVL contiene la riga ad essa relativa dell'output originario di DIR; se invece PIPPO è un file, lo standard input di EMPTYLVL risulta vuoto. Nel primo caso, pertanto, ERRORLEVEL è posto a  e la IF non effettua il salto: viene visualizzata la stringa 'PIPPO E' UNA DIRECTORY'. Nel secondo caso ERRORLEVEL vale e l'esecuzione salta all'etichetta :PIPPOFIL, a partire dalla quale, con un algoritmo del tutto analogo a quello testè commentato, si verifica se la dimensione di PIPPO riportata da DIR . Se il file non è vuoto, il suo contenuto viene visualizzato, una schermata alla volta, grazie all'azione combinata del comandi TYPE (interno) e MORE (esterno).

Data e ora nei comandi: DATECMD

Il programma DATECMD è concepito per consentire l'inserimento automatico di data e ora, o parti di esse, nei comandi DOS. Esso scandisce la propria command line alla ricerca di sequenze note di simboli, che definiamo, per comodità, macro, e le sostituisce con la parte di data o ora che rappresentano, valorizzata in base a data e ora di sistema. Ogni macro è costituita dal carattere ' ', seguito da una lettera che identifica il valore di sostituzione: così, ad esempio, @M indica il giorno del mese, espresso con due cifre. Le macro ammesse ed il loro significato sono elencati di seguito.


il carattere at ( ); utile per inserire una ' ' nella command line


le virgolette ( ); utile per inserire le virgolette nella command line

@A

l'anno; espresso con quattro cifre (es.:

@a

l'anno; espresso con due sole cifre (es.:

@M

il mese; espresso con due cifre (es.:

@m

il mese; espresso con una sola cifra se la prima è (es.:

@G

il giorno del mese; espresso con due cifre (es.:

@g

il giorno del mese; espresso con una sola cifra se la prima è (es.:

@R

il giorno dell'anno; espresso con tre cifre (es.:

@r

il giorno dell'anno; espresso con una o due cifre se le prime sono (es.:

@O

l'ora; espressa con due cifre (es.:

@o

l'ora; espressa con una sola cifra se la prima è (es.:

@I

il minuto; espresso con due cifre (es.:

@i

il minuto; espresso con una sola cifra se la prima è 0 (es.:

@S

il secondo; espresso con due cifre (es.:

@s

il secondo; espresso con una sola cifra se la prima è (es.:

@E

il giorno della settimana; è indicato il nome intero (es.: Lunedì

@e

il giorno della settimana; indicato mediante i primi tre caratteri del nome (es.: Lun

@W

il giorno della settimana; espresso con un numero da (Domenica) a (Sabato)

Vediamo un esempio pratico: se il comando

datecmd echo Sono le @O:@I:@S di @E @g/@M/@a, @r^ giorno dell'anno: @'Ciao, @@!'@

viene eseguito alle 14:52:20 del 2 novembre 1994, DATECMD esegue in realtà il comando

echo Sono le 14:52:20 di Mercoledì 2/11/94, 306^ giorno dell'anno: 'Ciao, @!'

DATECMD accetta inoltre due opzioni, ‑d‑v, che devono precedere la command line da eseguire. La prima consente di specificare uno 'slittamento' di data, positivo o negativo. Se nel comando dell'esempio precedente si antepone ‑v‑1 alla command line di DATECMD (cioè a echo), l'ouput prodotto diventa

echo Sono le 14:52:20 di Martedì 1/11/94, 305^ giorno dell'anno: 'Ciao, @!'

L'opzione ‑v, invece, richiede a DATECMD di visualizzare soltanto, ma non eseguire, la command line risultante a seguito della risoluzione delle macro.

Segue il listato, ampiamente commentato, del programma (vedere pag.  e seguenti circa l'implementazione di PARSEOPT.OBJ; le funzioni di manipolazione delle date sono descritte a pag. 



DATECMD.C - Barninga_Z! - 27/04/1993


Esegue command lines DOS con la possibilita' di parametrizzarle rispetto

alla data e all'ora: nella command line, digitata dopo il nome del

programma (DATECMD), possono essere inserite delle macro, costituite dal

carattere macro (@) e da uno dei caratteri elencati nel sorgente (nella

definizione dell'array delle macro). Dette macro vengono espanse nella

stringa con il corrispondente significato. In ogni command line possono

comparire piu' macro, ed ogni macro può comparire piu' volte. La macro

@' viene espansa nelle virgolette ('); la macro @@ viene espansa nella

atsign (@). L'utilita' di queste macro e' evidente nel caso in cui si

debbano inserire le virgolette nella command line o nel caso in cui una

sequenza di caratteri che non deve essere espansa comprenda in casualmente

i caratteri costituienti una macro. Ad esempio, @M viene espanso nel mese

corrente; per non espanderlo bisogna digitare @@M (@@ viene espanso in

@ e la M non viene modificata). Se la command line e' preceduta dalla

opzione -v essa viene solo visualizzata e non eseguita. Se è preceduta

da -d si possono specificare i giorni di differenza rispetto alla data

attuale (+|- gg).

_stklen e _heaplen sono settate per ridurre l'ingombro in memoria del

programma, visto che la command line e' eseguita con una system(). Per

questo motivo, inoltre, l'interprete dei comandi deve essere accessibile.


Compilato sotto Borland C++ 3.1:


bcc -k- -O -d datecmd.c parseopt.obj date2jul.obj jul2date.obj isleapyr.obj



#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <time.h>

#include <process.h>

#include <dir.h>

#include <errno.h>

#include <ctype.h>


#include 'parseopt.h' // per la gestione delle opzioni di command line


#define  _PRG_ 'DATECMD' // nome del programma

#define  _VERSION_ '1.5' // versione

#define  _YEAR_ '94' // anno di rilascio

#define  MAXCMD 128 // max lungh. cmd line (ENTER incl.)

#define  MACRO_FLAG '@' // carattere che segnala la macro

#define  DQUOTE_CHR 'x27' // sostituisce le virgolette

#define  SWITCH '-' // switch character per opzioni

#define  YEARLEN 365 // giorni dell'anno non bisestile

#define  WEEKLEN 7 // giorni della settimana

#define  WDAYSHORTLEN 3 // lung. stringa breve giorno sett.



// prototipi delle funzioni del corpo del programma



long date2jul(int day,int month,int year);

int isleapyear(int year);

int jul2date(long jul,int *day,int *month,int *year);


int  main(int argc,char **argv);


void  adjustOrdinal(int baseYear,int shift);

void  adjustWeekDay(int len);

void  fatalError(int errNum);

void  help(void);

char *initProg(int argc,char **argv);

int   isDigitStr(char *string);

char *parseCmd(char *cmdArgs);



// Ottimizzazione dell'uso della memoria



extern unsigned _heaplen = 4096;

extern unsigned _stklen  = 2048;



// Messaggi di errore e di aiuto



#define  _E_BADARGS 0

#define  _E_CMDLONG 1

#define  _E_BADOPTIONS 2

#define  _E_ALLOCMEM 3


char *weekdays[] = ;


char *errors[] = ;


char *helptext = '

Uso: DATECMD [-v][-d+|-g] CmdLinen

CmdLine è la command line da eseguire (-v visualizza soltanto, -d modifica lan

data di +/- g giorni); può contenere una o più delle seguenti macro:n




// Variabili globali per inizializzazione e altri scopi



struct tm *tData;                  // contiene data e ora correnti allo startup


char tStr[20];                     // buffer per sprintf() temporanee



// gruppo delle funzioni per il macro processing. Ciascuna di esse viene

// invocata se e' incontrata la macro corrispondente (vedere l'array macros)

// e restituisce un puntatore a stringa, che deve essere passato a strcpy()

// per copiare l'espansione della macro nella stringa di comando che sara'

// passata a spawn()



char *retMacroFlag(void)               // restituisce il carattere MACRO_FLAG



char *retDoubleQuote(void)      // restituisce il carattere '''



char *getYear(void)                    // anno, 4 cifre



char *getYear1(void)                   // anno, 2 cifre



char *getMonth(void)                 // mese, 2 cifre



char *getMonth1(void)                  // mese, 1 o 2 cifre



char *getDay(void)                     // giorno del mese, 2 cifre



char *getDay1(void)   // giorno del mese, 1 o 2 cifre



char *getOrdinal(void)                 // giorno dell'anno, 3 cifre



char *getOrdinal1(void)                // giorno dell'anno, 1 o 2 o 3 cifre



char *getHour(void)                    // ora, 2 cifre



char *getHour1(void)                   // ora, 1 o 2 cifre



char *getMin(void)                     // minuto, 2 cifre



char *getMin1(void)                    // minuto, 1 o 2 cifre



char *getSec(void)                     // secondo, 2 cifre



char *getSec1(void)                    // secondo, 1 o 2 cifre



char *getWeekDay(void)                 // giorno della settimana



char *getWeekDay1(void)                // giorno della settimana 3 lettere



char *getWeekDayNum(void)              // giorno della settimana numero




// definizione della struttura di gestione delle macro come tipo di dato e

// dichiarazione dell'array di strutture che definisce tutte le macro



typedef struct MACRO;


MACRO macros[] = ,

,

,

,

,

,

,

,

,

,

,

,

,

,

,

,

,

,

,





// organizzazione delle opzioni



#define  DISPLAY_ONLY 1 // opzione solo visualizza cmd line

#define  DAY_SHIFT 2 // opzione shift data in giorni


unsigned options;               // variabile per contenere i bits delle opzioni


char *optionS = 'd:v';                 // stringa definizione opzioni


#pragma  warn -par

#pragma  warn -rvl


// l'opzione -d consente di specificare uno scostamento in giorni dalla data

// corrente. Lo scostamento puo' essere positivo (es: 1 = doamni) o negativo

// (es: -1 = ieri). La funzione esegue tutti i controlli formali e modifica

// di conseguenza i dati di lavoro.


int valid_d(struct OPT *vld,int cnt)                     // convalida opzione d



// l'opzione -v forza DATECMD a non eseguire la command line costruita con

// l'espansione delle macro, bensi' a visualizzarla solamente. Qui viene

// settato il flag che indica che l'opzione e' stata richiesta


int valid_v(struct OPT *vld,int cnt)                     // convalida opzione v



// gestione delle opzioni specificate in modo errato


int err_handle(struct OPT *vld,int cnt)                  // opzioni errate



#pragma  warn .par

#pragma  warn .rvl


// array che associa ogni ozione alla corrispondente funzione di validazione


static struct VOPT vfuncs[] = ,

,

,






// corpo del programma (dopo main() le funzioni sono in ordine alfabetico)



int main(int argc,char **argv)                    // pilota tutte le operazioni



// adjustOrdinal() modifica il numero di giorno nell'anno (tm_yday) in base

// al numero di giorni di shift della data (opzione -d), tenendo presente che

// lo shift potrebbe generare un cambiamento di anno in piu' o in meno.


void adjustOrdinal(int baseYear,int shift)


else



// adjustWeekDay() modifica il numero di giorno nella settimana (tm_wday) in

// base al numero di giorni di shift della data (opzione -d), tenendo presente

// che lo shift potrebbe generare un cambiamento di settimama.


void adjustWeekDay(int shift)


tData->tm_wday %= WEEKLEN;

if(temp && tData->tm_wday)

tData->tm_wday = WEEKLEN-tData->tm_wday;



// fatalError() esce a DOS quando si verifica un errore, visualizzando un

// messaggio dall'array errors.


void fatalError(int errNum)



// help() stampa una videata di aiuto usando il campo comment di dell'array

// di strutture MACRO macros per visualizzare la descrizioni delle macro


void help(void)



// initProg() controlla i parametri e concatena gli elementi di argv per

// ricostruire la command line di DATECMD in un'unica stringa.


char *initProg(int argc,char **argv)


*initArgs = NULL;

for(i = argc-optn[0].val; ; )

return(initArgs);



// isDigitStr() controlla se una stringa contiene solo 0-9


int isDigitStr(char *string)



// parseCmd() ricerca ed espande le macro presenti nella command line di

// DATECMD, costruendo il comando da eseguire, nel quale compaiono, in luogo

// delle macro, gli elementi di data e ora desiderati.


char *parseCmd(char *cmdArgs)


if(macros[mIndex].symbol)

else

fatalError(_E_CMDLONG);

break;

}

default:

cmdLine[j++] = cmdArgs[i];

}

}

return(cmdLine);


DATECMD può rivelarsi particolarmente utile quando vi sia la necessità di dare ad un file, mediante i comandi DOS COPY o REN, un nome dipendente dalla data e ora in cui l'operazione stessa è effettuata. Si pensi, ad esempio, ad una procedura che, quotidianamente, scrive il risultato delle proprie elaborazioni nel file OUTPUT.DAT: qualora sia necessario conservare a lungo i file generati, può risultare comodo riservare loro una directory e copiarveli di giorno in giorno, rinominandoli in modo appropriato. Il solo modo di inserire il comando in un file batch, senza necessità alcuna di intervento interattivo da parte dell'utente, consiste nel servirsi di DATECMD

datecmd copy d:procoutput.dat c:procout@a@M@G.dat

Se ogni file deve essere conservato per una settimana soltanto, il comando presentato necessita appena un piccolo aggiustamento:

datecmd copy d:procoutput.dat c:procoutoutput.@e

Infatti, dal momento che la macro @e viene sostituita dai tre caratteri iniziali del nome del giorno della settimana, è evidente che ogni giorno il nuovo file sovrascrive quello copiato sette giorni prima.

Va ancora sottolineato che DATECMD consente una gestione avanzata di piping e redirezione: ad esempio, il comando

datecmd copy d:procoutput.dat c:procout@a@M@G.dat >> batch.ctl

redirige lo standard output di DATECMD in coda al file BATCH.CTL, mentre il comando

datecmd 'copy d:procoutput.dat c:procout@a@M@G.dat >> batch.ctl' > nul

aggiunge a BATCH.CTL lo standard output del comando COPY e sopprime quello di DATECMD; si noti l'uso delle virgolette, indispensabili per evitare che il DOS interpreti la redirezione facente parte della command line che deve essere eseguita da DATECMD

File piccoli a piacere: FCREATE

Gli esempi presentati poco sopra nascondono una insidia: il comando DOS COPY fallisce se il file origine ha dimensione pari a 0 byte; la conseguenza è che nella directory dedicata alla memorizzazione dei file di output potrebbero mancarne alcuni

FCREATE aggira il problema, consentendo la creazione di un file della dimensione voluta. Il file generato, qualora abbia dimensione maggiore di 0, contiene esclusivamente byte nulli (zero binario).



FCREATE.C - Barninga Z! - 27/10/1994


Crea un file della dimensione indicata sulla riga di comando.


Compilato sotto Borland C++ 3.1


bcc fcreate.c



#include <stdio.h>

#include <io.h>

#include <fcntl.h>

#include <stdlib.h>

#include <sysstat.h>


#define  PRG 'FCREATE'

#define  VER '1.0'

#define  YEAR '94'


#define  OPENMODE O_WRONLY+O_CREAT+O_TRUNC+O_BINARY

#define  OPENATTR S_IREAD+S_IWRITE


#define  E_SYNTAX 1

#define  E_OPEN 2

#define  E_CHSIZE 3

#define E_CLOSE 4

#define  E_PARM 5


// tutte le operazioni sono effettuate in main()


int main(int argc,char **argv)



// controllo del valore di size


if((size = atol(argv[1])) < 0L)

retcode = 0;


// apertura del file: se non esiste viene creato; se esiste e' troncato a 0


if((handle = open(argv[2],OPENMODE,OPENATTR)) == -1)

else

else


// se l'impostazione della nuova dimensione e' riuscita si calcola la nuova

// dimensione del file per verifica


size = filelength(handle);


// chiusura del file


if(close(handle))

}


// test e azione conseguente in caso di errore o meno


if(!retcode)

printf('%s: created %s (%ld bytes)n',PRG,argv[2],size);

return(retcode);


FCREATE richiede due parametri: il primo esprime la dimensione desiderata per il file; il secondo è il nome di questo. Insieme con EMPTYLVL (pag.  ) e DATECMD (pag.  ) è possibile implementare un algoritmo privo di criticità:

DIR 'D:PROCOUTPUT.DAT' | FIND ' 0 ' | EMPTYLVL

IF ERRORLEVEL 1 GOTO NONZERO

ECHO IL FILE OUTPUT.DAT HA DIMENSIONE 0 BYTES: UTILIZZO FCREATE

DATECMD FCREATE 0 C:PROCOUT@a@M@G.OUT

GOTO END

:NONZERO

ECHO IL FILE OUTPUT.DAT HA DIMENSIONE MAGGIORE DI 0 BYTES: UTILIZZO COPY

DATECMD COPY D:PROCOUTPUT.DAT C:PROCOUT@a@M@G.OUT

:END

FCREATE può validamente essere utilizzato per generare file aventi la funzione di placeholder, cioè per riservare spazio ad utilizzi futuri.

Attendere il momento buono: TIMEGONE

Per rendere le cose più complicate, assumiamo che il drive D:, sul quale viene prodotto OUTPUT.DAT dalla ormai nota procedura, non sia un disco fisicamente presente nel personal computer su cui lavoriamo, ma si tratti, al contrario, di un drive remoto , reso disponibile da una seconda macchina: il server di rete. La procedura, a sua volta, è eseguita su un terzo personal computer, anch'esso collegato al network ed in grado di accedere al medesimo disco condiviso. E' evidente, a questo punto, che non ha alcun senso lanciare il nostro file batch prima che la procedura in questione abbia terminato le proprie elaborazioni. Nell'ipotesi che ciò avvenga poco prima delle 23:00, chi non desideri trascorrere le proprie serate alla tastiera, in trepidante attesa, deve necessariamente adottare qualche provvedimento.

TIMEGONE è il provvedimento necessario : esso controlla se è trascorsa l'ora (ed eventualmente la data) indicata e termina, valorizzando di conseguenza il registro ERRORLEVEL. In particolare, ERRORLEVEL vale  se l'ora è trascorsa;  se non lo è.

L'ora da tenere sotto controllo deve essere specificata sulla command line di TIMEGONE nel formato hhmmss (due cifre per l'ora, due per i minuti, due per i secondi): ad esempio, il comando

timegone 060400

indica le ore 6:04. I minuti e i secondi devono essere sempre specificati.

TIMEGONE consente di indicare, in aggiunta all'ora, anche una data: allo scopo sono riconosciute tre opzioni di command line: ‑d consente di specificare il giorno, sempre espresso con due cifre. Ad esempio, il 12 del mese si indica con ‑d12. L'opzione ‑m specifica il mese: febbraio, per esempio, si indica con ‑m02. Infine, l'opzione ‑y permette di indicare l'anno, in 4 cifre. Il 1994, pertanto, si specifica con ‑y1994. Il comando

timegone -d03 -m11 170000

richiede a TIMEGONE di verificare se siano trascorse le ore 17 del 3 novembre: dal momento che l'anno non è specificato, esso non viene controllato (il risultato del test è indipendente dall'anno di elaborazione).

Mentre l'indicazione di giorno, mese e anno è facoltativa, l'ora deve essere sempre specificata. Tuttavia, in luogo dell'espressione in formato hhmmss può essere digitato un asterisco (' ') per forzare il programma a ricavare date e ora dalla stringa assegnata alla variabile d'ambiente TIMEGONE: questa ha formato YYYYMMGGhhmmss (l'indicazione dell'ora è preceduta da quattro cifre per l'anno, due per il mese, due per il giorno); è lecito specificare zeri per anno, mese e giorno (sempre in numero di quattro, due e, rispettivamente, ancora due) se si desidera che siano ignorati. La condizione dell'esempio precedente può essere espressa valorizzando la variabile TIMEGONE con il comando

set timegone=00001103170000

ed eseguendo successivamente

timegone *

Le opzioni eventualmente specificate sulla riga di comando hanno precedenza rispetto alla variabile d'ambiente; questa, infine, è ignorata se a TIMEGONE è passata, quale parametro, l'ora. Così

timegone -d04 *

verifica se sono trascorse le 17 del 4 novembre (fermo restando il valore della variabile TIMEGONE dell'esempio precedente).

TIMEGONE riconosce una quarta opzione: ‑r richiede che il risultato del test sia invertito (ERRORLEVEL vale  se data e ora non sono trascorse,  altrimenti).

Segue il listato, ampiamente commentato, del programma (vedere pag.  e seguenti circa l'implementazione di PARSEOPT.OBJ; le funzioni di manipolazione delle date sono descritte a pag. 



TIMEGONE.C - Barninga Z! - 29/09/94


Setta errorlevel a seconda che la data e l'ora correnti superino o meno

quelle richieste. Vedere helpStr per i dettagli della sintassi e delle

opzioni.


Compilato sotto Borland C++ 3.1


bcc timegone.c isleapyear.obj parseopt.obj



#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <dos.h>


#include 'parseopt.h'


#define  YEAR 1

#define  MONTH 2

#define  DAY 4

#define  REVERSE 8

#define  SWCHAR '-'

#define  ILLMSG 'Invalid option'

#define  YEARLEN 4

#define  MONTHLEN 2

#define  DAYLEN 2

#define  DATELEN (YEARLEN+MONTHLEN+DAYLEN)

#define  TIMELEN 6

#define  DTLEN (DATELEN+TIMELEN)

#define  TVARSYM '*'

#define  TVARNAME 'TIMEGONE'


#define  PRG 'TIMEGONE'

#define  REL '1.0'

#define  YR '94'

#define  RETERR 255

#define  FALSE 0

#define  TRUE 1

#define  ASCIIBASE 0x30

#define  MIN_YEAR 1980

#define  MAX_YEAR 2099


char *helpStr = '

n

options: one or more of the following:n

-dDD day DD of the month (always 2 digits).n

-mMM month MM of the year (always 2 digits).n

-yYYYY year YYYY (always 4 digits).n

-r reverse the result.n

n

time      time, in hhmmss format (always 6 digits). If time is the string '*',n

the environment variable TIMEGONE is used. It must contain an

datetime string in YYYYMMGGhhmmss format; YYYY=0000, MM=00, GG=00n

cause year, month and day respectively to be ignored.n

n

Command line parameters, if given, always override the environment variable.n

n

Errorlevel is set to 1 if the (date and) time specified has gone; it is set ton

0 if not yet. The -r option causes the result to be reversed (0 if gone, 1 ifn

not yet). If an error occurs, Errorlevel is set to 255.nn



int isleapyear(int year);


int main(int argc,char **argv);

int hasTimeGone(void);

int isDateValid(void);

int isNumeric(char *string);


int valid_d(struct OPT *vld,int cnt);

int valid_m(struct OPT *vld,int cnt);

int valid_r(struct OPT *vld,int cnt);

int valid_y(struct OPT *vld,int cnt);

int err_handle(struct OPT *vld,int cnt);


#pragma warn -rvl

#pragma warn -par

#pragma warn -aus


// l'opzione -d consente di specificare il giorno per comporre una data da

// controllare insieme all'ora. Qui sono effettuati tutti i controlli formali;

// 00 e' valido e indica che qualsiasi giorno va bene


int valid_d(struct OPT *vld,int cnt)



// l'opzione -m consente di specificare il mese per comporre una data da

// controllare insieme all'ora. Qui sono effettuati tutti i controlli formali;

// 00 e' valido e indica che qualsiasi mese va bene


int valid_m(struct OPT *vld,int cnt)



// l'opzione -r rovescia il risultato del test: se il momento e' trascorso

// ERRORLEVEL e' settato a 0 invece che a 1, e viceversa


int valid_r(struct OPT *vld,int cnt)



// l'opzione -y consente di specificare il anno per comporre una data da

// controllare insieme all'ora. Qui sono effettuati tutti i controlli formali;

// 0000 e' valido e indica che qualsiasi anno va bene


int valid_y(struct OPT *vld,int cnt)



// controlla la validita' formale dell'ora indicata sulla command line. Il

// formato deve essere hhmmss. In luogo dell'ora sulla cmdline si puo'

// specificare un asterisco: in tal caso il programma verifica se esiste la

// variabile d'ambiente TIMEGONE e ricava data e ora dalla stringa as essa

// assegnata.


int valid_time(struct OPT *vld,int cnt)



// si effettua il controllo di numericita' sulla stringa YYYYMMDD e si

// valorizzano i campi della struttura date


if((strlen(vld->arg) != DTLEN) || !isNumeric(vld->arg))

if(sscanf(vld->arg,'%4d%02d%02d',

(options & YEAR) ? &dummy : (int *)&da.da_year,

(options & MONTH) ? &dummy : (int *)&da.da_mon,

(options & DAY) ? &dummy : (int *)&da.da_day) < 3)

err_handle(NULL,NULL);


// vld->arg e' forzato a puntare alla seconda parte della stringa, avente

// formato hhmmss


vld->arg += DATELEN;

}

else


// da qui in poi l'elaborazione e' identica sia nel caso di ora passata sulla

// command line che di utilizzo della variabile TIMEGONE, in quanto, in

// entrambi i casi, vld->arg punta a una stringa in formato hhmmss


if((strlen(vld->arg) != TIMELEN) || !isNumeric(vld->arg))

err_handle(NULL,NULL);


// valorizzazione dei campi della struttura dostime_t


if(sscanf(vld->arg,'%02d%02d%02d',(int *)&ti.hour,

(int *)&ti.minute,

(int *)&ti.second) < 3)

err_handle(NULL,NULL);


// controllo di validita' formale della data


if(!isDateValid())

err_handle(NULL,NULL);


// controllo di validita' formale dell'ora


if((ti.hour > 23) || (ti.minute > 59) || (ti.second > 59))

err_handle(NULL,NULL);



// gestione degli errori: visualizzazione della stringa di help e del valore

// di ERRORLEVEL


int err_handle(struct OPT *vld,int cnt)



#pragma warn .par

#pragma warn .rvl

#pragma warn .aus


unsigned options;


// ogni opzione e' associata alla corrispondente funzione di validazione


struct VOPT valfuncs[] = ,

,

,

,

,

,



// elenco delle opzioni: quelle seguite da ':' vogliono un argomento


char *optionS = 'd:m:ry:';


struct date da;

struct dostime_t ti;


// main() analizza le opzioni via parseopt() ed effettua il test su data/ora

// via hasTimeGone()


int main(int argc,char **argv)


else

printf('%s: errorlevel set to %d.n',PRG,retval);

return(retval);



// controlla se la data/ora attuale ha superato la data/ora specificate via

// opzioni e/o variabile d'ambiente


int hasTimeGone(void)



// isDateValid() controlla la correttezza formale della data, verificando che

// per anno, mese e giorno siano verificati valori plusibili


int isDateValid(void)


extern struct date da;


// se il gmese e' < 0 o > 12 errore


if((da.da_mon < 0 ) || (da.da_mon > 12))

return(0);


// se l'anno non e' 0 deve essere compreso tra gli estremi di validita'; se lo

// e' si determina se e' bisestile


if(da.da_year)

else


// se l'anno e' 0 occorre ammettere che potrebbe essere bisestile, chissa'


monLenTbl[1] += 1;


// se il giorno non e' 0 esso deve essere positivo e minore o uguale al numero

// di giorni del mese; se il mese e' 0 bisogna ammettere che il 31 e' valido


if(da.da_day)

else

if(da.da_day > 31) // sempre e comunque errore!!

return(0);

}

return(1); // 1 = data OK



// controlla la numericita' della stringa ricevuta come parametro


int isNumeric(char *string)


return(TRUE);


Vediamo, finalmente, TIMEGONE al lavoro nel nostro esempio pratico:

:ATTESA

TIMEGONE 230000

IF ERRORLEVEL 1 GOTO ADESSO

GOTO ATTESA

:ADESSO

ECHO LE ORE 23:00 SONO APPENA TRASCORSE

DIR 'D:PROCOUTPUT.DAT' | FIND ' 0 ' | EMPTYLVL

IF ERRORLEVEL 1 GOTO NONZERO

ECHO IL FILE OUTPUT.DAT HA DIMENSIONE 0 BYTES: UTILIZZO FCREATE

DATECMD FCREATE 0 C:PROCOUT@a@M@G.OUT

GOTO END

:NONZERO

ECHO IL FILE OUTPUT.DAT HA DIMENSIONE MAGGIORE DI 0 BYTES: UTILIZZO COPY

DATECMD COPY D:PROCOUTPUT.DAT C:PROCOUT@a@M@G.OUT

:END

Il batch può essere eseguito a qualsiasi ora: TIMEGONE forza un loop sulle prime quattro righe del file sino alle 23:00 (se il batch è lanciato dopo le 23 il flusso elaborativo salta immediatamente all'etichetta :ADESSO

Estrarre una sezione da un file: SELSTR

Sempre più difficile: il famigerato OUTPUT.DAT contiene una sezione di testo che costituisce la base per alcune postelaborazioni. Ipotizziamo che essa abbia il seguente layout:

* RIEPILOGO *


* FINE *

Il contenuto della sezione, per il momento, non è rilevante: l'obiettivo è estrarla in modo automatico da OUTPUT.DAT e scriverla in un secondo file, da processare in seguito. La utility SELSTR rappresenta una soluzione: il comando

selstr '* RIEPILOGO *' '* FINE *' < output.dat > riepilog.dat

scrive sullo standard output le righe di testo, lette dallo standard input, a partire da quella contenente la prima stringa specificata e conclude l'estrazione con la riga contenente la seconda. Ancora una volta, perciò, è possibile sfruttare le capacità di redirezione offerte dal sistema operativo.

Tuttavia, SELSTR è in grado di fare di più. Esso, infatti, accetta sulla riga di comando alcune opzioni tramite le quali è possibile modificarne il comportamento, indicando se l'estrazione debba iniziare o terminare ad una riga specificata: per la descrizione dettagliata delle opzioni e dei loro parametri si veda il testo assegnato alla variabile helpStr, nel listato del programma. Qui vale la pena di soffermarsi sull'opzione ‑c, che consente di determinare la modalità di caching degli stream. In particolare, ‑ci richiede a SELSTR di attivare il caching dello standard input; ‑co richiede l'attivazione del caching per lo standard output; infine, ‑cb attiva il caching per entrambi gli stream. L'uso attento dell'opzione ‑c permette di ottenere un significativo incremento della efficienza di elaborazione.

Segue il listato, ampiamente commentato, del programma (vedere pag.  e seguenti circa l'implementazione di PARSEOPT.OBJ



SELSTR.C - Barninga Z! - 20/09/94


Seleziona una porzione di file in base a stringhe specificate come

delimitatori di inizio o fine. Vedere helpStr per i dettagli.


Compilato sotto Borland C++ 3.1


bcc selstr.c parseopt.obj



#include <stdio.h>

#include <string.h>

#include <ctype.h>

#include <stdlib.h>


#include 'parseopt.h'


#define  PRG 'SELSTR'

#define  VER '1.0'

#define  YEAR '94'

#define  MAXLIN 1024

#define  RET_ERR 255

#define  BUFSIZE 16348


#define  FROMNUM 1

#define  FROMBEG 2

#define  FROMLIN 4

#define  TONUM 8

#define  CACHE_I 16

#define  CACHE_O 32

#define  FROMBEG_C 'b'

#define  FROMLIN_C 'l'

#define  CACHE_I_C 'i'

#define  CACHE_O_C 'o'

#define  CACHE_B_C 'b'

#define  SWCHAR '-'

#define  ILLMSG 'Invalid option'


int main(int argc,char **argv);

int search(char *string1,char *string2);


int valid_c(struct OPT *vld,int cnt);

int valid_f(struct OPT *vld,int cnt);

int valid_t(struct OPT *vld,int cnt);

int err_handle(struct OPT *vld,int cnt);

int ctl_strings(struct OPT *vld,int cnt);


unsigned long startNum = 1L;

unsigned long stopNum = 0xFFFFFFFFL;


char *helpStr = '

options:n

-c: cache data streams:n

-ci: cache standard inputn

-co: cache standard outputn

-cb: cache both stdin and stdoutn

-f: line selection origin mode:n

-fn: from linenumber: lines are selected from line n (n > 0) to then

line containing string1 or to the line defined by -t, whichevern

comes first.n

-fb: from beginning: lines are selected from BOF to the linen

containing string1 or to the line defined by -t, whichever comesn

first. Same as -f1.n

-fl: from line (default mode): lines are selected from the linen

containing string1 to EOF. Selection stops at the linen

containing string2, if given and found, or at the line defined by -t.n

-t: line selection end mode:n

-tn: to linenumber: lines are selected from the origin defined by -fn

to line n (n > 0). Anyway, selection stops at the linen

containing string1 (or string2 for -fl), if found.n

ErrorLevel: 255: error;n

1: ok, one or more lines selected;n

0: ok, no lines selected.



unsigned options = FROMLIN;


#pragma warn -rvl

#pragma warn -par

#pragma warn -aus


// validazione dell'opzione -c per la richiesta di caching degli streams.

// Sono attivati i buffers di cache richiesti


int valid_c(struct OPT *vld,int cnt)


options |= CACHE_I;

if(*vld->arg == CACHE_I_C)

break;

case CACHE_O_C:

if(setvbuf(stdout,NULL,_IOFBF,BUFSIZE))

options |= CACHE_O;

break;

default:

err_handle(NULL,NULL);

}



// l'opzione -f consente di specificare quale deve essere la prima linea

// copiata da stdin a stdout: quella contenente il primo nonoption item

// (default), quella il cui numero e' specificato come argomento, o la prima

// riga del file


int valid_f(struct OPT *vld,int cnt)


return;

}

startNum = atol(vld->arg);

options &= ~FROMLIN;

options |= FROMNUM;



// l'opzione -t consente di specificare quale deve essere l'ultima riga del

// file copiata da stdin a stdout. -t consente di indicare un numero di riga;

// se -t non e' specificata la selezione termina alla riga contenente il

// secondo nonoption item sulla command line; in assenza di questo la selezione

// termina a fine file


int valid_t(struct OPT *vld,int cnt)



// gsetione errore di sintassi: visualizzazione del messaggio di help e

// uscita con errore


int err_handle(struct OPT *vld,int cnt)



// controllo dei parametri non-opzione presenti sulla command line: possono

// essere nessuno, uno o due a seconda delle opzioni richieste


int ctl_strings(struct OPT *vld,int cnt)


return(++instance);



#pragma warn .par

#pragma warn .rvl

#pragma warn .aus


// array di strutture che associano ad ogni opzione la corrispondente

// funzione di validazione


struct VOPT valfuncs[] = ,

,

,

,

,



// stringa elencante le opzioni ammesse. Quelle seguite da ':' richiedono un

// argomento


char *optionS = 'c:f:t:';


// main() scandisce la command line via parseopt() e, se le opzioni sono

// regolari, procede nell'elaborazione via search()


int main(int argc,char **argv)


if(!opt[0].val)

err_handle(NULL,NULL); // non c'e' string1

return(search(argv[argc-opt[0].val],argv[argc-opt[0].val+1]));



// search() legge stdin riga per riga e, secondo le opzioni e le stringhe

// specificate sulla riga di comando seleziona le righe da visualizzare su

// stdout


int search(char *string1,char *string2)



// la selezione di righe prosegue fino a che non e' incontrata la riga finale

// definita da stopNum (-t) o segnalata dalla presenza di string2


for(; fgets(line,MAXLIN,stdin) && (i < stopNum); i++)

}


// se la selezione deve partire dalla prima riga allora si copiano su stdout

// tutte le righe sino a quella contenente string1, che in questo caso

// permette di selezionare la riga di stop. Se era stato specificata una riga

// di stop con -t, allora e' controllata stopNum (che altrimenti contiene il

// massimo valore esprimible con un long, rendendo ininfluente il test)


else

}

return(lFlag ? 1 : 0); // 1 = copiate righe; 0 = nessuna riga copiata


Ecco la nuova versione del nostro file batch:

:ATTESA

TIMEGONE 230000

IF ERRORLEVEL 1 GOTO ADESSO

GOTO ATTESA

:ADESSO

ECHO LE ORE 23:00 SONO APPENA TRASCORSE

DIR 'D:PROCOUTPUT.DAT' | FIND ' 0 ' | EMPTYLVL

IF ERRORLEVEL 1 GOTO NONZERO

ECHO IL FILE OUTPUT.DAT HA DIMENSIONE 0 BYTES: UTILIZZO FCREATE

DATECMD FCREATE 0 C:PROCOUT@a@M@G.OUT

GOTO END

:NONZERO

ECHO IL FILE OUTPUT.DAT HA DIMENSIONE MAGGIORE DI 0 BYTES: UTILIZZO COPY

DATECMD COPY D:PROCOUTPUT.DAT C:PROCOUT@a@M@G.OUT

ECHO GENERAZIONE DEL FILE DI RIEPILOGO

SELSTR -cb '* RIEPILOGO *' '* FINE *' < D:PROCOUTPUT.DAT > C:PROCRIEPILOG.DAT

IF ERRORLEVEL 255 GOTO SELERROR

IF ERRORLEVEL 1 GOTO END

ECHO IL FILE OUTPUT.DAT NON CONTIENE LA SEZIONE DI RIEPILOGO

GOTO END

:SELERROR

ECHO ERRORE NELLA GENERAZIONE DEL RIEPILOGO

:END

La sezione estratta da OUTPUT.DAT, presente sul disco remoto, è scritta nel file RIEPILOG.DAT, sul disco locale. Dal momento che SELSTR restituisce in ERRORLEVEL un codice indicativo dello stato di uscita, è possibile effettuare in modo semplice la gestione degli errori: come si vede, in caso di errore di elaborazione viene restituito  : il flusso del file batch salta all'etichetta SELERROR ed è visualizzato un opportuno messaggio. Se ERRORLEVEL vale  , significa che l'elaborazione è terminata regolarmente ed è stata estratta almeno una riga di testo; altrimenti ERRORLEVEL contiene necessariamente  , che indica terminazione regolare, ma senza estrazione di testo: non rimane che segnalare che il contenuto di OUTPUT.DAT non è quello atteso.

Estrarre colonne di testo da un file: CUT

A questo punto vi è la necessità (ebbene sì) di postelaborare RIEPILOG.DAT: a scopo di esempio, ipotizziamo che tra riga iniziale e finale ne sia presente un numero variabile, tutte aventi il formato descritto di seguito:

+-CODICE-¦+-DATA-¦+-FILE-¦+-IMPORTO-¦

La sezione estratta, priva di riga inizale e finale, ha pertanto un aspetto analogo a quello delle righe seguenti:

099355476719940722OPER000100120344720

098446273319940722OPER003400009873325

088836288219940831OPER102800014436255


094553728219940912OPER001700225467520

062253725519941004OPER013100067855490

084463282919941103OPER000700127377385

E' necessario generare una tabella, da scrivere in un nuovo file, in cui solamente i campi DATA IMPORTO e CODICE siano riportati in modo leggibile e nell'ordine indicato. E' inoltre necessario generare un secondo file, ogni riga del quale contenga esclusivamente il campo FILE, a scopo di elaborazione successiva.

Strumento atto allo scopo è la utility CUT, ispirata all'omonimo comando Unix, del quale implementa le caratteristiche principali (in sostanza, la capacità di estrarre colonne di testo da un file), affiancandovi alcune novità.

CUT legge lo standard input, da ogni riga del quale estrae i caratteri occupanti le posizioni specificate mediante l'opzione della command line ‑c e li scrive sullo standard output. Tutti i messaggi sono scritti sullo standard error, al fine di non influenzare l'eventuale redirezione dell'output prodotto.

E' possibile specificare più istanze dell'opzione ‑c per richiedere l'estrazione di più colonne di testo: queste possono sovrapporsi parzialmente o totalmente; inoltre la posizione di partenza può essere superiore a quella di arrivo (in questo caso CUT estrae i caratteri da destra a sinistra). Vediamo un esempio: nell'ipotesi che lo standard input di CUT sia costituito dalla riga

ABCDE12345FGHIJ67890

il comando

cut -c1-3 -c2-5 -c8-5 -c15-25

scrive sullo standard output la riga

ABCBCDE321EJ67890

Si noti che le posizioni dei caratteri sono numerate a partire da 1. Inoltre, per default, laddove la lunghezza della riga di standard input non sia sufficiente a soddisfare interamente uno degli intervalli specificati (come nel caso ‑c15-25), vengono comunque estratti almeno i caratteri presenti: sono disponibili, però, tre opzioni di command line con le quali è possibile gestire tali situazioni in modo differente. In particolare, ‑d forza CUT a scartare la riga di input, mentre ‑x determina l'interruzione dell'elaborazione con ERRORLEVEL pari a  (il valore di default per una terminazione regolare è  ). Ancora, l'opzione ‑p consente di specificare una stringa dalla quale estrarre i caratteri necessari a compensare quelli mancanti nello standard input: il comando

cut -p#=@ -c15-25

estrae dallo standard input dell'esempio precedente la riga

J67890#=@#=

Alcuni caratteri della stringa ' ' sono ripetuti, in quanto la sua lunghezza non è sufficiente; al contrario, i caratteri eventualmente eccedenti la lunghezza necessaria sono ignorati.

CUT riconosce inoltre alcune opzioni che consentono di inserire stringhe in testa (‑b) e in coda (‑e) alle righe di standard output, nonché tra gli intervalli di caratteri (‑m). Vediamone un esempio di utilizzo nel solito file batch:

:ATTESA

TIMEGONE 230000

IF ERRORLEVEL 1 GOTO ADESSO

GOTO ATTESA

:ADESSO

ECHO LE ORE 23:00 SONO APPENA TRASCORSE

DIR 'D:PROCOUTPUT.DAT' | FIND ' 0 ' | EMPTYLVL

IF ERRORLEVEL 1 GOTO NONZERO

ECHO IL FILE OUTPUT.DAT HA DIMENSIONE 0 BYTES: UTILIZZO FCREATE

DATECMD FCREATE 0 C:PROCOUT@a@M@G.OUT

GOTO END

:NONZERO

ECHO IL FILE OUTPUT.DAT HA DIMENSIONE MAGGIORE DI 0 BYTES: UTILIZZO COPY

DATECMD COPY D:PROCOUTPUT.DAT C:PROCOUT@a@M@G.OUT

ECHO GENERAZIONE DEL FILE DI RIEPILOGO

SELSTR -cb '* RIEPILOGO *' '* FINE *' < D:PROCOUTPUT.DAT > C:PROCRIEPILOG.DAT

IF ERRORLEVEL 255 GOTO SELERROR

IF ERRORLEVEL 1 GOTO POSTELAB

ECHO IL FILE OUTPUT.DAT NON CONTIENE LA SEZIONE DI RIEPILOGO

GOTO END

:SELERROR

ECHO ERRORE NELLA GENERAZIONE DEL RIEPILOGO

:POSTELAB

ECHO GENERAZIONE DELLA TABELLA DI RIEPILOGO

TYPE C:PROCRIEPILOG.DAT FIND /V '* RIEPIL' | FIND /V '* FINE *' > C:PROCTR.TMP

ECHO TABELLA DI RIEPILOGO > C:PROCTR.TXT

ECHO. >> C:PROCTR.TXT

ECHO    DATA IMPORTO CODICE >> C:PROCTR.TXT

ECHO +-------- ----- ------ ----+ >> C:PROCTR.TXT

CUT -b'¦ ' -m' ¦ ' -e' ¦' -c11-18 -c27-37 -c1-10 < C:PROCTR.TMP >> C:PROCTR.TXT

ECHO +-------- ----- ------ ----+ >> C:PROCTR.TMP

ECHO GENERAZIONE DELLA LISTA DI FILES

CUT -c19-26 < C:PROCTR.TMP > C:PROCFILES.DAT

DEL C:PROCTR.TMP

:END

Il comando FIND è utilizzato con l'opzione /V per generare un file temporaneo (TR.TMP) contenente tutte le righe di RIEPILOG.DAT, eccetto la prima e l'ultima (intestazione e chiusura); TR.TMP è poi elaborato con CUT per produrre la tabella desiderata (TR.TXT) e il file contenente le sole occorenze del campo FILES

Il contenuto del file TR.TXT è il seguente:

TABELLA DI RIEPILOGO


DATA IMPORTO CODICE

19940722

00120344720

0993554767

19940722

00009873325

0984462733

19940831

00014436255

0888362882




19940912

00225467520

0945537282

19941004

00067855490

0622537255

19941103

00127377385

0844632829

Il contenuto di FILES.DAT è invece:

OPER0001

OPER0034

OPER1028


OPER0017

OPER0131

OPER0007

Segue il listato commentato di CUT (vedere pag.  e seguenti circa l'implementazione di PARSEOPT.OBJ



CUT.C - Barninga Z! - 1994


Utility che riprende ed amplia le funzionalita' del comando CUT di Unix.

Circa la sintassi vedere la variabile helpStr.


Compilato sotto Borland C++ 3.1


bcc -O -d -rd -k- cut.c parseopt.obj



#include <stdio.h>

#include <string.h>

#include <ctype.h>

#include <errno.h>

#include <stdlib.h>


#include 'parseopt.h'


#define  PRG 'CUT' // nome del programma

#define  REL '1.0' // versione

#define  YEAR '94' // anno di rilascio

#define  ERRCOD 255 // codice errore uscita programma

#define  MAXLIN 2048 // massima lunghezza di una riga

#define  MAXPAD 128 // max lungh. stringa di padding

#define  CUTLEN 4 // max 4 cifre per opz. -c

#define  CUTOPT 1 // -c

#define  EXITOPT 2 // -x

#define  DISCRDOPT 4 // -d

#define  PADOPT 8 // -p

#define  MIDOPT 16 // -s

#define  BEGOPT 32 // -b

#define  ENDOPT 64 // -e

#define  SWITCH '-' // switch character per opzioni


// CUTTER e' il template di struttura che controlla la formazione delle

// colonne di testo: start contiene l'offset del primo byte; stop quello

// dell'ultimo. In pratica sono gli estremi del range da copiare. start

// puo' essere maggiore di stop, nel qual caso i bytes sono copiati in

// ordine inverso


typedef struct CUTTER;


char *helpStr = '

Syntax is: cut option(s)n

Default exit code: 0. Input: stdin; Output: stdout; Msgs: stderr. Options are:n

-bBEGSTR      BEGSTR is the string to be inserted at the beginning of eachn

output line.n

-cSTART-STOP  START-STOP specify a range of characters to be copied from eachn

input line to output. If START is greater than STOP, charactersn

in the range are reversed. More than one range can be given;n

anyway, at least one range is required. See also -d -p -x.n

-d            If one or more -c ranges fall outside an input line, the linen

itself will be discarded. By default, columns are output evenn

if one or more ranges are not full. -d -p -x are mutuallyn

exclusive.n

-eENDSTR      ENDSTR is the string to be appended at the end of each outputn

line.n

-mMIDSTR      MIDSTR is the string to be inserted between two -c ranges; usefuln

only if more than one range is given.n

-pPADSTR      PADSTR is the string used to pad output columns of -c rangesn

falling outside a stdin line. When -p is not requested, outputn

lines can have different length. -p -d -x are mutually exclusive.n

-x  If one or more -c ranges fall outside an input line, the programn

will immediately return 255. -x -d -p are mutually exclusive.n

Displays this help screen and exits with a return code of 255.



char *crgtMsg = '%s %s: cuts stdin into columns. Barninga Z! '%s. Help: -?n';

char *eLinEnd = 'Cut pointers past end of line';

char *eOptCut = 'No cut option specified';

char *eOptFmt = 'Invalid option format';

char *eOptGen = 'Invalid option or option format';

char *eOptSeq = 'Invalid option sequence';


CUTTER *cutArr;               // struttura di controllo

char padStr[MAXPAD]; // stringa per il padding

char begStr[MAXPAD]; // stringa di apertura

char midStr[MAXPAD]; // stringa di fincatura

char endStr[MAXPAD]; // stringa di chiusura


int valid_b(struct OPT *vld,int cnt);

int valid_c(struct OPT *vld,int cnt);

int valid_d(struct OPT *vld,int cnt);

int valid_e(struct OPT *vld,int cnt);

int valid_m(struct OPT *vld,int cnt);

int valid_p(struct OPT *vld,int cnt);

int valid_x(struct OPT *vld,int cnt);

int hlp_handle(struct OPT *vld,int cnt);

int err_handle(struct OPT *vld,int cnt);


int main(int argc,char **argv);

void cutstream(char *line,char *buffer,char *bufprint,int maxlen);


#pragma  warn -par

#pragma  warn -rvl


// se specificata opzione -b la stringa argomento deve essere copiata nello

// spazio apposito (begStr). Questa stringa e' scritta in testa ad ogni riga

// di standard output prodotta dal programma.


int valid_b(struct OPT *vld,int cnt)



// almeno una opzione -c deve sempre essere specificata. Qui vengono effettuati

// tutti i controlli di liceita' sull'argomento (estremi del range di colonne

// da estrarre da stdin) e per ogni range e' allocata una struttura CUTTER,

// formando cosi' un array di strutture che descrivono come deve essere

// 'ritagliato' stdin.


int valid_c(struct OPT *vld,int cnt)


else

if(!isdigit(vld->arg[i]))

err_handle(NULL,(int)eOptFmt); // errore: non e' - o una cifra

if(!flag)

err_handle(NULL,(int)eOptFmt); // errore: non c'e' il -

if((i-flag) > CUTLEN)

err_handle(NULL,(int)eOptFmt); // errore: numero troppo alto

if(!cutArr) // solo prima volta!!

if(!(cutArr = (CUTTER *)malloc(sizeof(CUTTER))))

err_handle(NULL,(int)sys_errlist[errno]);

sscanf(vld->arg,'%d-%d',&(cutArr[instance].start),&(cutArr[instance].stop));

if(!cutArr[instance].start || !cutArr[instance].stop)

err_handle(NULL,(int)eOptFmt);

++instance;

if(!(cutArr = (CUTTER *)realloc(cutArr,(instance+1)*sizeof(CUTTER))))

err_handle(NULL,(int)sys_errlist[errno]);

cutArr[instance].start = cutArr[instance].stop = NULL;

return(options |= 1);



// l'opzione -d richiede che se uno o entrambi gli estermi di un range (-c)

// 'cadono' al di fuori della riga attualmente processata la riga stessa

// deve essere scartata. L'opzione -d e' alternativa a -p e -x.


int valid_d(struct OPT *vld,int cnt)



// se specificata opzione -e la stringa argomento deve essere copiata nello

// spazio apposito (endStr). Questa stringa e' scritta in coda ad ogni riga

// di standard output prodotta dal programma.


int valid_e(struct OPT *vld,int cnt)



// se specificata opzione -m la stringa argomento deve essere copiata nello

// spazio apposito (midStr). Questa stringa e' scritta, in ogni riga di

// standard output prodotta dal programma, tra due range di caratteri estratti

// (-c) da stdin, se almeno 2 istanze di -c sono state specificate. Se ne e'

// stata richiesta una sola, l'opzione -m e' ignorata.


int valid_m(struct OPT *vld,int cnt)



// se specificata opzione -p la stringa argomento deve essere copiata nello

// spazio apposito (padStr). Questa stringa e' utilizzata, in ogni riga di

// standard output prodotta dal programma, per riempire il range richiesto

// (-c), se il numero di caratteri estratti da stdin e' minore dell'ampiezza

// specificata per il range stesso. E' significativa solo se uno o entrambi

// gli estremi di un range 'cadono' al di fuori della riga di stdin attualmente

// processata. E' alternativa a -d e -x.


int valid_p(struct OPT *vld,int cnt)



// l'opzione -x richiede che se uno o entrambi gli estermi di un range (-c)

// 'cadono' al di fuori della riga attualmente processata l'elaborazione deve

// essere interrotta e restituito un codice di errore. Alternativa a -p e -x.


int valid_x(struct OPT *vld,int cnt)



// visualizza la videata di help se e' specificata opzione -?


int hlp_handle(struct OPT *vld,int cnt)



// gestisce tutte le situazioni in cui e' necessario visualizzare un messaggio

// ed uscire con un errore. Se il parametro vld e' NULL significa che la

// chiamata e' fatta esplicitamente dal programmatore e quindi e' visualizzato

// il messaggio il cui indirizzo e' passato come secondo parametro (con

// opportuno cast!). Se vld non e' NULL, allora err_handle() e' chiamata da

// parseopt() e quindi e' visualizzato un messaggio generico.


int err_handle(struct OPT *vld,int cnt)



#pragma  warn .par

#pragma  warn .rvl


// array che associa le opzioni alla funzione di validazione corrispondente


struct VOPT vfuncs[] = ,

,

,

,

,

,

,

,

,




// stringa di elenco delle opzioni; se una lettera e' seguita da ':' significa

// che l'opzione richiede un argomento


char *optionStr = 'b:c:de:m:p:x?';


unsigned options;                                        // flags delle opzioni


// main() valida le opzioni via parseopt() e gestisce il ciclo di estrazione

// dei range per ogni riga di input. Tutti i messaggi del programma, incluso

// il copyright, sono scritti su stderr; in tal modo la redirezione

// dell'output prodotto non li include.


int main(int argc,char **argv)


while(gets(line))

cutstream(line,inibuf,buffer,maxlen);

return(0);



// cutstream() estrae i range dalla riga di testo ricevuta come parametro e

// scrive la riga output su stdout. Si noti che il paramtero bufprint e'

// l'indirizzo originale del buffer: questo infatti deve essere stampato

// tutto anche se cutstream() ne lavora solo una parte (se c'e' begStr). E'

// evidente che in assenza di opzione -b, buffer e bufprint contengono lo

// stesso indirizzo.


void cutstream(char *line,char *buffer,char *bufprint,int maxlen)



// se uno solo degli estremi e' fuori dalla riga, allora una parte del range

// deriva da caratteri della riga, la cui fine diviene uno dei due estremi del

// range stesso (start o stop, a seconda della direzione). Il numero di

// caratteri di padding e' pari al numero di caratteri non estraibili dalla

// riga, cioe' alla parte 'scoperta' del range.


else

}

if(len) // si copia solo se la riga non e' vuota!


// il ciclo gestisce l'operazione di copia; l'applicazione a start di un

// incremento positivo o negativo a seconda dei casi permette di gestire

// entrambe le situazioni (da sx a dx o viceversa)


for(--start, --stop; j < maxlen-1; start += inc)


// se e' richiesta -p si copia la stringa di padding nel buffer per il numero

// di caratteri ancora mancanti nel range. Se la stringa e' piu' lunga del

// necessario viene troncata; se e' piu' corta viene ripetuta sino a riempire

// tutto lo spazio.


if(options & PADOPT)

for(k = 0; j < maxlen-1 && lim; lim--)

++i;


// se e' specificata -m e ci sono ancora range da estrarre (l'ultimo elemento

// dell'array di struct CUTTER contiene NULL in entrambe i campi per segnalare

// la fine) allora si concatena al buffer la stringa midStr. Non si usa

// strcat() per essere sicuri di non andare oltre lo spazio rimanente nel

// buffer.


if((options & MIDOPT) && cutArr[i].start)

for(k = 0; j < maxlen-1 && midStr[k]; )

buffer[j++] = midStr[k++];

else


// se invece quello appena estratto e' l'ultimo range ed e' specificata -e

// si concatena al buffer la endStr.


if((options & ENDOPT) && !cutArr[i].start)

for(k = 0; j < maxlen-1 && endStr[k]; )

buffer[j++] = endStr[k++];

}

buffer[j] = NULL; // tappo!

puts(bufprint);


Lanciando CUT con l'opzione  sulla riga di comando viene visualizzato un testo di aiuto, che ne riassume le principali caratteristiche ed opzioni.

Il comando FOR rivisitato: DOLIST

Veniamo a FILES.DAT (generato da CUT), ogni riga del quale, ai fini del nostro esempio, rappresenta un nome di file: ipotizziamo che su ciascuno di essi sia necessario ripetere la medesima elaborazione. L'interprete DOS implementa il comando FOR, che non è in grado di accettare una lista variabile di parametri (vedere pag.  ): risulta quindi impossibile utilizzare FILES.DAT allo scopo. La utility DOLIST offre un rimedio: essa esegue una riga di comando, che viene considerata suddivisa in due parti, di cui la seconda opzionale, inserendo tra di esse la riga letta dallo standard input. Se da questo possono essere lette più righe, il comando viene iterato per ciascuna di esse.

La complessità del meccanismo è solo apparente: nell'ipotesi che l'elaborazione del nostro esempio consista nell'eseguire un secondo file batch il cui parametro sia ogni volta un diverso file, la procedura diviene la seguente:

:ATTESA

TIMEGONE 230000

IF ERRORLEVEL 1 GOTO ADESSO

GOTO ATTESA

:ADESSO

ECHO LE ORE 23:00 SONO APPENA TRASCORSE

DIR 'D:PROCOUTPUT.DAT' | FIND ' 0 ' | EMPTYLVL

IF ERRORLEVEL 1 GOTO NONZERO

ECHO IL FILE OUTPUT.DAT HA DIMENSIONE 0 BYTES: UTILIZZO FCREATE

DATECMD FCREATE 0 C:PROCOUT@a@M@G.OUT

GOTO END

:NONZERO

ECHO IL FILE OUTPUT.DAT HA DIMENSIONE MAGGIORE DI 0 BYTES: UTILIZZO COPY

DATECMD COPY D:PROCOUTPUT.DAT C:PROCOUT@a@M@G.OUT

ECHO GENERAZIONE DEL FILE DI RIEPILOGO

SELSTR -cb '* RIEPILOGO *' '* FINE *' < D:PROCOUTPUT.DAT > C:PROCRIEPILOG.DAT

IF ERRORLEVEL 255 GOTO SELERROR

IF ERRORLEVEL 1 GOTO POSTELAB

ECHO IL FILE OUTPUT.DAT NON CONTIENE LA SEZIONE DI RIEPILOGO

GOTO END

:SELERROR

ECHO ERRORE NELLA GENERAZIONE DEL RIEPILOGO

:POSTELAB

ECHO GENERAZIONE DELLA TABELLA DI RIEPILOGO

TYPE C:PROCRIEPILOG.DAT FIND /V '* RIEPIL' | FIND /V '* FINE *' > C:PROCTR.TMP

ECHO TABELLA DI RIEPILOGO > C:PROCTR.TXT

ECHO. >> C:PROCTR.TXT

ECHO    DATA IMPORTO CODICE >> C:PROCTR.TXT

ECHO +-------- ----- ------ ----+ >> C:PROCTR.TXT

CUT -b'¦ ' -m' ¦ ' -e' ¦' -c11-18 -c27-37 -c1-10 < C:PROCTR.TMP >> C:PROCTR.TXT

ECHO +-------- ----- ------ ----+ >> C:PROCTR.TMP

ECHO GENERAZIONE DELLA LISTA DI FILES

CUT -c19-26 < C:PROCTR.TMP > C:PROCFILES.DAT

DEL C:PROCTR.TMP

ECHO ELABORAZIONE DELLA LISTA DI FILES IN FILES.DAT

DOLIST 'CALL ELABFILE.BAT' < FILES.LST

IF ERRORLEVEL GOTO FILERROR

GOTO END

:FILERROR

ECHO ERRORE NELLA ELABORAZIONE DELLA LISTA DI FILES

:END

DOLIST esegue la riga di comando una volta per ogni riga presente nel file FILES.DAT: con i dati dell'esempio, sono generati e lanciati i seguenti comandi:

CALL ELABFILE.BAT OPER0001

CALL ELABFILE.BAT OPER0034

CALL ELABFILE.BAT OPER1028


CALL ELABFILE.BAT OPER0017

CALL ELABFILE.BAT OPER0131

CALL ELABFILE.BAT OPER0007

Si noti l'utilizzo delle virgolette: esse sono necessarie in quanto CALL ed ELABFILE.BAT devono costituire insieme la prima parte del comando. Senza le virgolette DOLIST interpreterebbe CALL come prima porzione della command line e ELABFILE.BAT come seconda, inserendo tra esse la riga letta da FILES.DAT. I comandi eseguiti sarebbero perciò

CALL OPER0001 ELABFILE.BAT

CALL OPER0034 ELABFILE.BAT

CALL OPER1028 ELABFILE.BAT


CALL OPER0017 ELABFILE.BAT

CALL OPER0131 ELABFILE.BAT

CALL OPER0007 ELABFILE.BAT

con scarse possibilità di successo.

Segue il listato di DOLIST, ampiamente commentato.



DOLIST.C - Barninga_Z! - 17/10/93


Esegue un comando dos su una lista di argomenti. Esempio


dolist copy c:backups*.bak < files.lst


copia tutti i files elencati nel file files.lst nella dir c:backups

attribuendo a ciascuno estensione .bak. Il files contenente gli

argomenti deve essere in formato ascii, una riga per ogni comando.

In pratica dolist compone ed esegue il comando:


comando riga_della_lista comando_2a_parte


In uscita ERRORLEVEL e' settato come segue:


0 = comando eseguito (indipenentemente dal suo risultato)

1 = elaborazione interrotta (errore di sintassi della cmdline di DOLIST)

2 = comando non eseguito (system fallita)

3 = comando non eseguito (command line da eseguire > 127 caratteri)


Compilato sotto Borland C++ 3.1:


bcc -d -rd -k- dolist.c



#pragma  warn -pia


#include <stdio.h>

#include <stdlib.h>

#include <string.h>


#define  PRG 'DOLIST'

#define  VER '1.0'

#define  YEAR '93'

#define  SEP_STR ' '

#define  NUL_CHRS ' tnr'

#define  MAXCMD 128

#define  MAXLIN 256


// main() gestisce tutte le operazioni necessarie


int main(int argc,char **argv)


else

if(!system(cmd)) // esecuzione!

printf('%s: eseguito: %sn',PRG,cmd);

else

}

else

}

}

}

return(retCode);


DOLIST restituisce in ERRORLEVEL un valore determinato dallo stato dell'elaborazione: in particolare,  indica che tutti i comandi sono stati eseguiti;  evidenza un errore di sintassi nella riga di comando;  indica il fallimento della chiamata alla funzione di libreria system(), che invoca l'interprete dei comandi DOS , infine, segnala che la lunghezza del comando composto mediante l'inserimento della riga di standard input tra prima e seconda parte della riga di comando risulta eccessiva

I comandi nidificati: CMDSUBST

Che cosa accade in ELABFILE.BAT? Le ipotesi del nostro esempio si complicano sempre più (ma è l'ultimo sforzo, promesso!): la prima riga di ciascuno dei file elencati in FILES.DAT contiene un codice numerico, espresso in formato ASCII, che esprime uno stato relativo alle elaborazioni del riepilogo. La stringa  indica che quella particolare operazione è stata conclusa senza errori (e pertanto non vi sono altre righe nel file); tutti gli altri codici segnalano errori (il file contiene altre righe, descrittive dell'errore stesso). Scopo di POSTELAB.BAT è generare un file contenente le sole segnalazioni di errore.

Anche in questo caso il sistema Unix è fonte di ispirazione: la utility CMDSUBST consente di eseguire comandi DOS formati in tutto o in parte dall'output di altri comandi . Vediamo subito un esempio: il comando

cmdsubst echo $type c:autoexec.bat$

produce un output costituito dalla prima riga del file AUTOEXEC.BAT. Infatti, CMDSUBST scandisce la command line alla ricerca dei caratteri ' ed esegue, come un normale comando DOS, quanto tra essi compreso. La prima riga dello standard output prodotto dal comando è inserita nella command line originale, in luogo della stringa eseguita. Infine, è eseguita la command line risultante. Nell'esempio testè presentato, pertanto, viene dapprima lanciato il comando

type c:autoexec.bat

Nell'ipotesi che la prima riga del file sia

@ECHO OFF

la command line risultante, eseguita da CMDSUBST

echo @ECHO OFF

la quale, a sua volta, produce lo standard output

@ECHO OFF

E' possibile specificare più subcomandi, ma non è possibile nidificarli: in altre parole, è valida una command line come:

cmdsubst echo $echo pippo$ $echo pluto$ $echo paperino$

che produce l'output

pippoplutopaperino

ma non lo è, o meglio, non è interpretata come forse ci si aspetterebbe, la seguente:

cmdsubst echo $echo $echo pippo$$

L'output prodotto è

echo is ON echo pippo$

Infatti, CMDSUBST individua il primo subcomando nella sequenza '$echo $', mentre la coppia ' ' è sostituita con un singolo carattere ' ' (è questa, del resto, la sintassi che consente di specificare una command line contenente il ' ' medesimo).

Torniamo al nostro caso pratico: il file ELABFILE.BAT può dunque essere strutturato come segue

@ECHO OFF

CMDSUBST IF @$TYPE D:PROC%1$@==@000@ GOTO END

IF ERRORLEVEL 1 GOTO ERRORE

SELSTR -f2 STRINGA_INESISTENTE < D:PROC%1 >> C:PROCERRORI.DAT

GOTO END

:ERRORE

ECHO ERRORE NELL'ESECUZIONE DELL'ELABORAZIONE DI %1

:END

La sostituzione effettuata da CMDSUBST genera un test IF dalla sintassi lecita; SELTSR estrae dal file tutto il testo a partire dalla seconda riga (opzione ‑f), nell'ipotesi che la stringa 'STRINGA_INESISTENTE' non compaia mai nei file elaborati. La variabile  è sostituita dal DOS con il parametro passato a ELABFILE.BAT dal batch chiamante (nell'esempio si tratta del nome del file da elaborare). Si noti, infine, che nel batch chiamante è opportuno inserire, prima della riga che invoca DOLIST, un comando di cancellazione del file C:PROCERRORI.DAT, dal momento che SELSTR vi scrive lo standard output in modalità di append (il testo è aggiunto in coda al file, se esistente; in caso contrario questo è creato e 'allungato' di volta in volta).

Si ha perciò:


ECHO ELABORAZIONE DELLA LISTA DI FILES IN FILES.DAT

IF EXIST C:PROCERRORI.DAT DEL C:PROCERRORI.DAT

DOLIST 'CALL ELABFILE.BAT' < FILES.LST

IF ERRORLEVEL 1 GOTO FILERROR


Il listato di CMDSUBST, ampiamente commentato, è riportato di seguito.



CMDSUBST.C - Barninga Z! - 1994


Esegue command line sostituendo la prima riga dello stdout di un altro

comando compreso tra i caratteri $. Vedere stringa di help per i

particolari.


Compilato sotto Borland C++ 3.1


bcc cmdsubst.c



#pragma  warn -pia


#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <alloc.h>

#include <dir.h>

#include <process.h>


#define  PRG 'CMDSUBST'

#define  VER '1.0'

#define  YEAR ''93'

#define  SUBST_FLAG '$'

#define  MAXCMD 128

#define  TEMPLATE 'CSXXXXXX'

#define  ROOTDIR '?:'

#define OUTREDIR '>'

#define  SEPSTR ' '

#define  EOL 'n'


char *helpStr = '

The command line is searched for '$' characters. If found, a string between twon

'$' is executed as a command line and the first line of its standard outputn

replaces it in the CMDSUBST command line itself. Example: if the first line ofn

the file c:autoexec.bat isnn

@ECHO OFFnn

the command linenn

CMDSUBST echo $type c:autoexec.bat$nn

causes the commandnn

echo @ECHO OFFnn

to be actually executed. If a CMDSUBST command line contains a '$', it must ben

typed twice.n



// il template SUBCTL costituisce lo strumento per il controllo della

// sostituzione dei comandi nella command line. Il campo init referenzia

// il primo byte del comando, il campo len ne contiene la lunghezza. Viene

// allocato un array di strutture, ogni elemento del quale controlla la

// sostituzione di un comando.


typedef struct SUBCTL;


int main(int argc,char **argv);

char *execSubCmd(char *subCmd,char *tempPath);

char *getCmdLine(int argc,char **argv);

char *getSubCmdOutput(char *outString,char *tempPath);

SUBCTL *initSubCtl(SUBCTL *ctl,int items,char *cmdBase);

char *makeCmdLine(SUBCTL *ctl,char *cmdLine,char *tempPath);

char *makeTempPath(char *tempPath);

SUBCTL *parseCmdLine(char *cmdLine);


// main() controlla l'esecuzione del programma in 4 fasi: ricostruzione

// della command line; creazione di un file temporaneo per la gestione

// dello stdout dei programmi; scansione della command line ed esecuzione

// dei subcomandi in essa contenuti; costruzione della nuova command line

// e sua esecuzione.


int main(int argc,char **argv)


if(!makeTempPath(tempPath))

if(!(ctl = parseCmdLine(cmdLine)))

if(cmdLine = makeCmdLine(ctl,cmdLine,tempPath))

system(cmdLine);

return(0);



// execSubCmd() esegue un subcomando compreso nella command line di CMDSUBST.

// Viene costruita una command line contenente il subcomando e l'istruzione

// di redirezione del suo standard output nel file temporaneo il cui nome

// e' stato costruito da makeTempPath(). L'output e' poi analizzato dalla

// getSubCmdOutput() e il file temporaneo e' cancellato. Il sottocomando e'

// eseguito via system().


char *execSubCmd(char *subCmd,char *tempPath)


else

fprintf(stderr,'%s: error: subcmd string too long.n',PRG);

return(ptr);



// getCmdLine() ricostruisce la command line passata a CMDSUBST concatenando

// in un buffer static tutti gli elementi di argv. Il buffer deve essere

// static perche' l'indirizzo e' restituito alla funzione chiamante. In

// alternativa lo si potrebbe allocare con malloc()


char *getCmdLine(int argc,char **argv)


cmdLine[strlen(cmdLine)-1] = NULL;

return(cmdLine);



// getSubCmdOutput() apre il file temporaneo contenente lo standard output

// del subcomando eseguito e ne legge la prima riga, che deve essere

// sostituita al subcomando stesso nella command line di CMDSUBST.


char *getSubCmdOutput(char *outString,char *tempPath)


return(outString);



// initSubCtl() inizializza una struttura di controllo del subcomando,

// riallocando l'array. La prima chiamata a initSubCtl() le passa un NULL

// come puntatore all'array, cosi' la funzione puo' usare realloc() anche

// per allocare la prima struttura.


SUBCTL *initSubCtl(SUBCTL *ctl,int items,char *cmdBase)


ctl[items-1].init = cmdBase+strlen(cmdBase);

ctl[items-1].len = 0;

return(ctl);



// makeCmdLine() costruisce la nuova command line che CMDSUBST deve eseguire

// sostituendo ai sottocomandi la prima riga del loro standard output. La

// command line cosi' costruita e' restituita a main(), che la esegue via

// system().


char *makeCmdLine(SUBCTL *ctl,char *cmdLine,char *tempPath)


} while(ctl[i++].len);

return(newCmdLine);



// makeTempPath() prepara un nome di file temporaneo completo di path formato

// dal drive di default e dalla root. Il file temporaneo e' percio' scritto

// in root ed e' destinato a contenere lo stdout del comando eseguito


char *makeTempPath(char *tempPath)



// parseCmdLine() scandisce la command line passata a CMDSUBST per individuare

// eventuali subcomandi racchiusi tra i '$'. Per ogni subcomando e' allocata

// una nuova struttura SUBCTL nell'array. Se sono incontrati due '$'

// consecutivi, si presume che essi rappresentino un solo '$' effettivo nella

// command line: uno e' scartato e non ha luogo alcuna sostituzione. La

// funzione non e' in grado di gestire sostituzioni nidificate.


SUBCTL *parseCmdLine(char *cmdLine)


}

}

return(flag ? NULL : ctl);


Qualora non sia possibile effettuare la sostituzione nella command line (riga di comando risultante eccessivamente lunga o caratteri ' ' scorrettamente appaiati), o la funzione system() restituisca un codice di errore CMDSUBST termina con un valore di ERRORLEVEL diverso da




 La definizione comandi esterni è spesso applicata ai soli programmi facenti parte del sistema operativo in senso stretto (DISKCOPY FORMAT, etc.).

 O la non esistenza (IF NOT EXIST

FOR consente di ripetere un comando su una lista di argomenti elencati tra parentesi tonde: non vi è modo di utilizzare una lista esterna al batch, quale, ad esempio, un file ASCII gestito automaticamente da altre procedure.

 Si tratta di programmi progettati espressamente per automatizzare elaborazioni eseguite in parallelo da diverse macchine su dati condivisi in rete; tuttavia essi possono, comunque, risultare utili in ambienti meno complessi: si pensi a procedure eseguite su una medesima macchina, ma in multitasking (ad esempio quali task DOS in Microsoft Windows 3.x). Del resto non è affatto esclusa la possibilità di servirsene produttivamente in semplici procedure eseguite in modo standalone e monotasking.

 In poche parole: lo standard output di DIR non viene visualizzato, bensì è trasformato dal DOS in standard input per il comando successivo (in questo caso FIND

DATECMD esegue la propria command line effettuando una DOS shell, cioè invocando una seconda istanza (transiente) dell'interprete dei comandi. Ne segue che COMMAND.COM (o, comunque, l'interprete utilizzato) deve essere disponibile (nella directory corrente o in una di quelle elencate nella variabile d'ambiente PATH) e deve esserci memoria libera in quantità sufficiente per il caricamento dell'interprete stesso e per l'esecuzione del comando da parte di questo. Vedere pag. 135.

 Può trattarsi di un inconveniente grave qualora, ad esempio, altre procedure abbiano necessità di accedere comunque ai file (o, quanto meno, di verificarne l'esistenza) per operare correttamente.

 Si dice remoto (in contrapposizione a locale) un disco appartenente ad una macchina diversa da quella sulla quale si svolge la sessione di lavoro. Se più personal computer sono collegati in rete, ciascuno di essi può utilizzare, in aggiunta alle proprie risorse locali, quelle remote rese disponibili dalle macchine configurate come server. L'utilizzo alle risorse remote avviene in modo condiviso, in quanto più personal computer possono accedervi contemporaneamente.

 Qualcosa si può fare anche senza TIMEGONE, con l'aiuto di EMPTYLVL (pag.  ). Innanzitutto si deve creare un file ASCII contenente un CR (ASCII  ), che, per comodità, indichiamo col nome CR.TXT. Occorre poi inserire nella procedura batch una sequenza di istruzioni analoga alla seguente:

:ASPETTA

TIME < CR.TXT | FIND '15:09' | EMPTYLVL

IF ERRORLEVEL 1 GOTO ASPETTA

Lo standard input del comando TIME è rediretto da CR.TXT, con l'effetto di terminare il comando senza modificare l'ora di sistema; lo standard output è invece rediretto, mediante piping, sullo standard input di FIND, che cerca la stringa contenente l'ora desiderata (nell'esempio le 15:09); infine, EMPTYLVL memorizza  in ERRORLEVEL se la stringa non è trovata (l'ora non è quella voluta). I limiti della soluzione descritta sono evidenti: in primo luogo il batch deve essere lanciato necessariamente prima dell'ora specificata, in quanto la stringa non viene trovata anche se quella è già trascorsa; inoltre si tratta di un algoritmo applicabile con difficoltà al comando DATE, in particolare quando si desideri effettuare il test su parte soltanto della data. La utility CUT (presentata a pag.  ) può venire in soccorso, ma si introducono, comunque, complicazioni notevoli al listato qui riprodotto.

 Si tratta di una tecnica di ottimizzazione delle operazioni di I/O, implementata mediante algoritmi, più o meno sofisticati, di gestione di buffer, che consentono di limitare il numero di accessi alle periferiche hardware (con particolare riferimento ai dischi). Sull'argomento vedere pag. 

DOLIST esegue la command line effettuando una DOS shell, cioè invocando una seconda istanza (transiente) dell'interprete dei comandi. Ne segue che COMMAND.COM (o, comunque, l'interprete utilizzato) deve essere disponibile (nella directory corrente o in una di quelle elencate nella variabile d'ambiente PATH) e deve esserci memoria libera in quantità sufficiente per il caricamento dell'interprete stesso e per l'esecuzione del comando da parte di questo. Vedere pag. 135.

 Il DOS accetta comandi di lunghezza inferiore o pari a 128 caratteri, compreso il CR terminale.

 In realtà non esiste, in Unix, un comando analogo a CMDSUBST. Tuttavia la shell standard (l'equivalente dell'interprete dei comandi in ambiente DOS) scandisce la command line alla ricerca di stringhe racchiuse tra apici inversi (il carattere ' '), le esegue come veri e propri comandi e le sostutuisce, nella command line stessa, con lo standard output prodotto.

CMDSUBST utilizza il carattere ' ' in luogo dell'apice inverso per comodità: quest'ultimo non è presente sulla tastiera italiana.

 Ancora una volta, CUT e EMPTYLVL potrebbero ottenere, da soli, un risultato analogo, ma con un algoritmo più complesso e meno flessibile.

 Anche CMDSUBST esegue la command line effettuando una DOS shell, cioè invocando una seconda istanza (transiente) dell'interprete dei comandi. Vedere pag. 135.

Scarica gratis Lavorare con i file batch
Appunti su: creare un file in dos concatenando la data,



Scarica 100% gratis e , tesine, riassunti



Registrati ora

Password dimenticata?
  • Appunti superiori
  • In questa sezione troverai sunti esame, dispense, appunti universitari, esercitazioni e tesi, suddivisi per le principali facoltà.
  • Università
  • Appunti, dispense, esercitazioni, riassunti direttamente dalla tua aula Universitaria
  • all'Informatica
  • Introduzione all'Informatica, Information and Comunication Tecnology, componenti del computer, software, hardware ...