2013-07-10 28 views
5

Recentemente ho ereditato un programma SAS che sembra qualcosa di simile:Perché l'utilizzo di CALL EXECUTE per eseguire un errore di macro quando viene eseguito direttamente funziona?

%MACRO ComplicatedStuff(GroupId=); 

    %LET FileId = %SYSFUNC(OPEN(Work.BigDataSet)); 

    %PUT 'Doing something really difficult with ' &GroupId.; 

    %LET CloseRC = %SYSFUNC(CLOSE(&FileId.)); 

%MEND ComplicatedStuff; 

%ComplicatedStuff(GroupId=ABC1); 
%ComplicatedStuff(GroupId=DEF2); 
%ComplicatedStuff(GroupId=3GHI); 
%ComplicatedStuff(GroupId=J4KI); 

Essendo un programmatore multi-sfaccettato, ho guardato questo e ho pensato "sicuramente posso fare questo un almeno un po 'più dinamico". Certo, basta, sono stato in grado di sviluppare quello che pensavo fosse una soluzione semplice utilizzando CALL EXECUTE:

DATA Work.IDs; 

    INPUT ID  $4. 
      ; 

DATALINES; 
ABC1 
DEF2 
3GHI 
J4KI 
RUN; 

DATA Work.CommandDebug; 
    SET Work.IDs; 

    Command = CATS(
       '%ComplicatedStuff(GroupId=', ID, ');' 
      ); 

    CALL EXECUTE(Command); 

RUN; 

ero felice con questa soluzione fino a quando venne il momento di FTP i file generati da ComplicatedStuff a un server diverso. Il nostro server SAS è in esecuzione su Unix e gli amministratori SAS hanno creato una piccola macro utile per chiamare %sas_sftp (perché, mi è stato detto, il codice x diventa davvero brutto). Purtroppo, non posso pubblicare il codice %sas_sftp - appartiene alla mia azienda e non credo che lo vogliano su SO.

ho tentato di chiamare la macro %sas_sftp proprio come avevo chiamato il %ComplicatedStuff macro (sia come seconda CALL EXECUTE all'interno dello stesso passo dei dati, e in un secondo tempo i dati), ma solo il primo file (circa 30) sarebbe fallo arrivare a destinazione. Quando ho guardato il log, sembrava che la seconda macro iniziasse ad essere eseguita prima che il ftp fosse finito (il ftp pipe, o qualunque cosa fosse, non era stato rilasciato prima del successivo ftp), quindi i successivi ftp stavano semplicemente fallendo silenziosamente a causa di indisponibilità delle risorse (presumo).

Ho pensato che EXECUTE avrebbe praticamente accodato le mie macro chiamate e poi le avrebbe eseguite come se fossero disposte sequenzialmente nel codice (come erano originariamente) - una alla volta. Chiaramente sta succedendo qualcos'altro perché, mentre il primo approccio sopra ha funzionato senza problemi, la mia soluzione dinamica ha fallito. Ho versato lo CALL EXECUTE: How and Why e lo SAS documentation, ma temo di non capire di cosa stanno parlando.

Alla fine ho trovato un lavoro (o meglio, un collega ne ha trovato uno) che ho postato sotto come una "risposta", ma mi piacerebbe davvero che qualcuno spiegasse la funzione ESEGUI e come funziona.

Perché il mio primo tentativo, utilizzando CALL EXECUTE, non funziona?

+0

Non è chiaro dalla domanda in cui si trova il problema. Hai detto che stai provando ad eseguire quest'altra macro '% sas_sftp' ma non la mostri nel tuo codice. Stai cercando di aggiungere un secondo 'CALL EXECUTE' allo stesso passo di dati o hai un secondo passo di dati che esegue solo la parte'% sas_sftp'? E a proposito, ho anche una macro utility con lo stesso nome! Il problema potrebbe essere esattamente ciò che sta facendo la macro. Non penso che la mente possa essere eseguita con 'CALL EXECUTE' o perché crea ed esegue uno script' expect'. – BellevueBob

+0

@BellevueBob - Non riesco a pubblicare la macro sas_sftp, in parte perché non ho accesso al codice e in parte perché non è mia da pubblicare su un forum pubblico. Ho provato ad aggiungere un secondo 'CALL EXECUTE' al passaggio dei dati. Quando ciò non ha funzionato, ho provato un secondo passaggio DATA _NULL_. Stesso risultato in entrambi i casi. – JDB

+0

In realtà speravo di vedere un esempio del codice che stavi usando che aveva l'errore, non la stessa macro. La risposta potrebbe essere semplice come utilizzare la funzione macro '% nrstr' durante la chiamata. In altre parole, mostri il codice che funziona ma non mostra ciò che non funziona. – BellevueBob

risposta

4

CALL EXECUTE funziona in modo simile al codice nel wiki della comunità, ad eccezione di alcuni problemi specifici relativi alla tempistica. Il problema più comune in cui mi imbatto è quando eseguo una macro che include qualcosa che definisce una variabile macro, come ad esempio uno PROC SQL select into all'interno di quella macro che quindi crea il testo macro utilizzato nella macro, non dissimile dalla risposta. A causa delle regole di temporizzazione, ciò non viene eseguito fino a quando CALL EXECUTE non termina la costruzione del codice da eseguire, il che significa che il valore non viene modificato correttamente all'interno del codice.

Ecco un esempio.

%macro mymacro(age=0); 
proc sql noprint; 
select quote(name) into :namelist separated by ',' from sashelp.class where age=&age.; 
quit; 

data name_age; 
set sashelp.class; 
where name in (&namelist.); 
run; 
proc print data=name_age; 
var name age; 
run; 
%mend mymacro; 

proc sort data=sashelp.class out=class nodupkey; 
by age; 
run; 

OK, ora ho un set di dati di controllo (class) e una macro per eseguire fuori di esso. Ecco lo call execute. Questo non funziona correttamente; la prima volta che viene eseguito, i messaggi relativi all'identificatore di nomi & non sono definiti, al secondo e al futuro si otterrà l'intera età = 16 (l'ultima era) poiché è ciò che definisce la variabile macro.

data _null_; 
set class; 
exec_val = cats('%mymacro(age=',age,')'); 
call execute(exec_val); 
run; 

Ecco la macro chiamata sql. Funziona come previsto.

proc sql noprint; 
select cats('%mymacro(age=',age,')') into :calllist separated by ' ' 
    from class; 
quit; 
&calllist; 

non trovo chiamata execute di essere il più utile come l'elenco soluzione di macro PROC SQL ai dati codice generato, tranne quando è più facile costruire il codice in un passaggio di dati e non sto facendo tutto ciò che causa problemi di temporizzazione.

+0

Questo descrive esattamente cosa stavo vedendo nei log. Ci devono essere alcune variabili macro definite all'interno di sas_sftp. Grazie! – JDB

4

Questo è un lavoro che evita la funzione EXECUTE. La sto postando come aiuto per i futuri visitatori, ma in realtà non risponde alla mia domanda principale.

Il codice seguente crea variabili macro con i comandi che desidero eseguire sfruttando la sintassi SQL INTO :. Creo quindi una semplice macro che sostanzialmente esegue iterazioni sulle variabili macro e le risolve (facendo sì che le istruzioni vengano eseguite come se fossero letteralmente nel codice sorgente).

PROC SQL NOPRINT; 
    SELECT  COUNT(*) 
    INTO  :CommandCount 
    FROM  Work.CommandDebug 
    ; 

    SELECT  Command 
    INTO  :Command1 - :Command%LEFT(&CommandCount.) 
    FROM  Work.CommandDebug 
    ; 
QUIT; 

%MACRO ExeCommands; 

    %DO I = 1 %TO &CommandCount.; 

     &&Command&I.; /* Resolves to %ComplicatedStuff(GroupId=ABC1);, etc */ 

    %END; 

%MEND; 
%ExeCommands; 
1

Un buon aggiramento per i tempi di valutazione macro in CALL EXECUTE consiste nel mettere il codice in un file temporaneo e quindi includere il file. Ciò è utile anche per il debug quando si desidera avere una copia cartacea del codice inviato (basta passare il file in un file fisico).

%macro mymacro(age=0); 
proc sql noprint; 
select quote(name) into :namelist separated by ',' from sashelp.class where age=&age.; 
quit; 

data name_age; 
set sashelp.class; 
where name in (&namelist.); 
run; 
proc print data=name_age; 
var name age; 
run; 
%mend mymacro; 

proc sort data=sashelp.class out=class nodupkey; 
by age; 
run; 

filename blah temp; 

data _null_; 
set class; 
file blah; 
exec_val = cats('%mymacro(age=',age,')'); 
put exec_val; 
run; 

%include blah; 

filename blah clear; 
Problemi correlati