2009-09-11 20 views
7

Sto tentando di modificare la macro in basso per accettare un parametro macro come argomento "posizione" per un comando dir. Tuttavia non riesco a farlo risolvere correttamente a causa del problema delle virgolette nidificate. L'utilizzo di% str (% ') non funziona, né le funzioni di quotatura per qualche motivo.Utilizzo di SAS Macro per reindirizzare un elenco di nomi file da una directory di Windows

La macro funzionerà correttamente quando il filepath ha senza spazi (ad es. C: \ temp \ withnospace) poiché le virgolette intermedie non sono necessarie. Tuttavia ho bisogno di questa macro per lavorare su filepath con spazi (ad es. "C: \ temp \ con spazio \").

Si prega di aiuto!

%macro get_filenames(location) 
    filename pipedir pipe "dir &location. /b " lrecl=32767; 
    data filenames; 
    infile pipedir truncover; 
    input line $char1000.; 
    run; 
%mend; 

%get_filenames(C:\temp\)    /* works */ 
%get_filenames('C:\temp\with space') /* doesnt work */ 

risposta

13

Ecco un altro modo per ottenere lo stesso risultato senza dover utilizzare un PIPE.

%macro get_filenames(location); 
filename _dir_ "%bquote(&location.)"; 
data filenames(keep=memname); 
    handle=dopen('_dir_'); 
    if handle > 0 then do; 
    count=dnum(handle); 
    do i=1 to count; 
     memname=dread(handle,i); 
     output filenames; 
    end; 
    end; 
    rc=dclose(handle); 
run; 
filename _dir_ clear; 
%mend; 

%get_filenames(C:\temp\);   
%get_filenames(C:\temp\with space); 
%get_filenames(%bquote(C:\temp\with'singlequote)); 
+0

sebbene questo in realtà non risponda alla domanda, questo codice è di gran lunga superiore in quanto può essere utilizzato in tutti gli ambienti e soddisfa lo stesso scopo. grazie ... –

1

Usiamo questo piccolo macro

%macro getdir(dir=,redirect=, switch=); 
    options noxwait xsync; 
    %if %length(&switch)=0 %then %let switch=b; 
    data _null_; 
     xcmd='dir "' || "&dir" || '"' || "/&switch " || ">" || "&redirect"; 
     put 'generated the following command: ' xcmd=; 
     rc=system(xcmd); 
     put 'result code of above command: ' rc=; 
    run; 
%mend getdir; 

esempio di chiamata

%getdir(dir=c:\temp\,redirect=c:\temp\dir.txt) *run; 

Se si esegue in batch e non hanno il option noxwait xsync il lavoro si bloccherà sul server in attesa di un risposta dell'operatore.

3

Sulla base l'ultimo campione in this page, anziché l'istruzione nome del file, provare

%let filrf=pipedir; 
%let rc=%sysfunc(filename(filrf,%bquote(dir "&location" /b),pipe)); 

e chiamare la macro senza l'utilizzo di citazioni:

%get_filenames(c:\temp\with spaces); 

Ho anche provato macro citando, ma couldn' Lo faccio funzionare.

+0

+1 - bello. Puoi anche passare lrecl come opzione host a questo, quindi è proprio come il suo originale: % lasciare rc =% sysfunc (nomefile (filrf,% bquote (dir "& posizione"/b), pipe, lrecl = 32767)); – cmjohns

2

funziona per me se io chiamo la macro originale in questo modo

 
%get_filenames(""C:\Program Files"") 

naturalmente ho dovuto aggiungere il punto e virgola alla fine dell'istruzione %macro.

se la directory contiene una virgola, accadono cose brutte. per correggere, utilizzare la macro %str()

 %get_filenames(%str(C:\temp\comma, fail))

6

Apportare le seguenti modifiche diverse e il codice funzionerà.

%macro get_filenames(location); %*--(1)--*; 
    filename pipedir pipe "dir ""%unquote(&location)"" /b" lrecl=32767; %*--(2)--*; 
    data filenames; 
    infile pipedir truncover; 
    input filename $char1000.; 
    put filename=; 
    run; 
    filename pipedir clear; %*--(3)--*; 
%mend; 

%get_filenames(d:\)   
%get_filenames(d:\your dir) %*--(4)--*; 

(1) Terminare la dichiarazione %macro con un punto e virgola;

(2) Circondare la risoluzione della variabile macro con virgolette doppie raddoppiate e %unquote;

(3) Rilasciare il file handle cancellandolo; e

(4) Non quotare i parametri di input. citazione macro, se necessario.

+0

funziona - ma lascia un messaggio di errore (ATTENZIONE: Nessuna assegnazione logica per il nome file PIPE.) –

+2

cambia semplicemente nome file pipe clear; a nomefile pipedir clear; per sopprimere l'avvertimento. – cmjohns

3

Ecco una macro rapida per inserire elenchi di directory basati su Windows in un set di dati sas.

 
%macro DirList(dir); 

/* %if &SUBDIR eq %then %let subdir=/s; */  /*** &SUBDIR not defined ****/ 
filename dirpipe pipe "dir &DIR.\*.* /s /-c"; 

data dir_list(label="Directory Listing [&DIR.]" drop=re_: _line_ date time); 
    format Path 
     File $250. 
     ModDT datetime19. 
     Size 16. 
     _line_ $32000. ; 

    if _N_ = 1 then do; 
    re_path=prxparse("/Directory of (.+)/"); 
    re_subd=prxparse("/(\d\d\/\d\d\/\d\d\d\d)\s+(\d\d:\d\d [A|P]M)\s+\s+(\S.*)/"); 
    re_file=prxparse("/(\d\d\/\d\d\/\d\d\d\d)\s+(\d\d:\d\d [A|P]M)\s+(\d+)\s+(\S.*)/"); 
    retain re_: path; 
    end; 

    infile dirpipe lrecl=32000; input; _line_ = _infile_; 

    if lengthn(_line_)=0 then delete; 
    else 
    if prxmatch(re_path, _line_) then do; 
    path=prxposn(re_path, 1, _line_); 
    end; 
    else 
    if prxmatch(re_subd, _line_) then do; 
    date=input(prxposn(re_subd, 1, _line_), mmddyy10.); 
    time=input(prxposn(re_subd, 2, _line_), time6.); 
    ModDT=dhms(date, 0, 0, time); 
    File=prxposn(re_subd, 3, _line_); 
    size = .D; /*mark subdirectory records*/ 
    if file not in ('.', '..') then output; 
    end; 
    else 
    if prxmatch(re_file, _line_) then do; 
    date=input(prxposn(re_file, 1, _line_), mmddyy10.); 
    time=input(prxposn(re_file, 2, _line_), time6.); 
    ModDT=dhms(date, 0, 0, time); 
    size=input(prxposn(re_file, 3, _line_), 16.); 
    file=prxposn(re_file, 4, _line_); 
    output; 
    end; 
run; 
filename dirpipe clear; 
%mend; 

ed ecco il modo in cui vengono chiamati

 
%dirlist(c:); 
%dirlist(c:\temp); 

avviso non v'è alcuna barra rovesciata quando si specifica la directory di base. C: non C:\.

+0

Mi piace, ma ho dovuto apportare un paio di modifiche per farlo funzionare sul mio sistema (win xp). Ho cambiato re_subd = line in re_subd = prxparse ("/ (\ d \ d \/\ d \ d \/\ d \ d \ d \ d) \ s + (\ d \ d: \ d \ d [A | P] M) \ s +

\ s + (\ S. *)/"); poiché ogni riga veniva trattata come una sottodirectory per me. Inoltre, ho rimosso% if & SUBDIR eq% then% let subdir =/s; line since & subdir La variabile macro non è definita e non viene utilizzata altrove. – cmjohns

+0

Ricordo di dover apportare la modifica a re_subd regex pattern quando os è stato aggiornato, ma non ho mai pensato di salvare la versione precedente. il parametro SUBDIR deve essere utilizzato per specificare l'opzione/s per gli elenchi sottocartella ricorsiva. un giorno. – rkoopmann

2

Qui è uno che unscrambles l'ordine di citazione e si toglie la quotatura:

%let command =%unquote(%str(%')dir "&baseDir.data\*txt"%str(%')); 

filename datain pipe &command; 

dove macro variabile basedir può contenere spazi e così può i nomi dei file. Questa combinazione di %unquote e %str(%') è un idioma macro che si verifica frequentemente.

"cosa succede se ho una sola citazione nella mia dir?"

Handling questa situazione richiede una funzione macro citando, come ad esempio %bquote(); Continuando l'esempio precedente, questo:

%let command =%unquote(%str(%')dir "%bquote(&baseDir.data\*txt)"%str(%')); 

dovrebbe farlo.

Per evitare infinite iterazioni di questo tipo di domande, consultare . Ian Whitlock's paper, Un serio sguardo al Macro Quoting, che è disponibile here;

Ci sono (molti) altri, ma questo è il più ampiamente citato. Una piccola nota: probabilmente qualsiasi cosa di Ian Whitlock merita. Scrive chiaramente e la sua comprensione dei problemi di SAS è eccezionale.

2

Ecco una versione di codice macro puro. Consente inoltre di specificare che si desidera solo conoscere i file (e non le cartelle) e consente di specificare un filtro di base. Restituisce l'elenco dei file in un formato delimitato, ma è possibile inserirli facilmente in un set di dati utilizzando l'inserto SQL se si desidera (esempio incluso ma non testato, nessun accesso di SAS atm). Può essere chiamato da qualsiasi luogo - all'interno di un'altra macro, un set di dati, un'istruzione sql ... ovunque. Basta aggiungere questi due macro alla tua libreria di autocall macro e hai ragione.

Di seguito sono disponibili 2 macro. La macro% isdir è richiesta dalla macro% file_list. Le macro sono un po 'più grandi e complesse di quelle precedenti ma sono MOLTO più flessibili. Inoltre forniscono il controllo degli errori.

/****************************************************************************** 
** PROGRAM: ISDIR.SAS 
** 
** DESCRIPTION: DETERMINES IF THE SPECIFIED PATH EXISTS OR NOT. 
**    RETURNS: 0 IF THE PATH DOES NOT EXIST OR COULD NOT BE OPENED. 
**      1 IF THE PATH EXISTS AND CAN BE OPENED. 
** 
** PARAMETERS: iPath: THE FULL PATH TO EXAMINE. NOTE THAT/AND \ ARE TREATED 
**     THE SAME SO &SASDIR/COMMON/MACROS IS THE SAME AS 
**     &SASDIR\COMMON\MACROS. 
** 
******************************************************************************/ 

%macro isDir(iPath=,iQuiet=1); 
    %local result dname; 

    %let result = 0; 

    %if %sysfunc(filename(dname,&iPath)) eq 0 %then %do; 
    %if %sysfunc(dopen(&dname)) %then %do; 
     %let result = 1; 
    %end; 
    %else %if not &iQuiet %then %do; 
     %put ERROR: ISDIR: %sysfunc(sysmsg()); 
    %end; 
    %end; 
    %else %if not &iQuiet %then %do; 
    %put ERROR: ISDIR: %sysfunc(sysmsg()); 
    %end; 

    &result 

%mend; 

%put %isDir(iPath=&sasdir/common/macros); 
%put %isDir(iPath=&sasdir/kxjfdkebnefe); 
%put %isDir(iPath=&sasdir/kxjfdkebnefe, iQuiet=0); 
%put %isDir(iPath=c:\temp); 

/****************************************************************************** 
** PROGRAM: FILE_LIST.SAS 
** 
** DESCRIPTION: RETURNS THE LIST OF FILES IN A DIRECTORY SEPERATED BY THE 
**    SPECIFIED DELIMITER. RETURNS AN EMPTY STRING IF THE THE 
**    DIRECTORY CAN'T BE READ OR DOES NOT EXIST. 
** 
** PARAMETERS: iPath  : THE FULL PATH TO EXAMINE. NOTE THAT/AND \ ARE 
**       TREATED THE SAME SO &SASDIR/COMMON/MACROS IS THE 
**       SAME AS &SASDIR\COMMON\MACROS. WORKS WITH BOTH UNIX 
**       AND WINDOWS. 
**    iFilter : SPECIFY A BASIC FILTER TO THE FILENAMES, NO REGULAR 
**       EXPRESSIONS OR WILDCARDS. 
**    iFiles_only: 0=RETURN FILES AND FOLDERS 
**       1=RETURN FILES ONLY. 
**    iDelimiter : SPECIFY THE DELIMITER TO SEPERATE THE RESULTS BY. 
******************************************************************************/ 
/* 
** TODO: DOESNT CATER FOR MACRO CHARS IN FILENAMES. FIX SOMETIME. 
** TODO: IMPROVE THE FILTER. JUST A SIMPLE IF STATEMENT AT THE MOMENT. 
*/ 
%macro file_list(iPath=, iFilter=, iFiles_only=0, iDelimiter=|); 
    %local result did dname cnt num_members filename; 

    %let result=; 

    %if %sysfunc(filename(dname,&iPath)) eq 0 %then %do; 

    %let did = %sysfunc(dopen(&dname)); 
    %let num_members = %sysfunc(dnum(&did)); 

    %do cnt=1 %to &num_members; 
     %let filename = %sysfunc(dread(&did,&cnt)); 
     %if "&filename" ne "" %then %do; 
     %if &iFiles_only %then %do; 
      %if not %isDir(iPath=&iPath/&filename) %then %do; 
      %if "&iFilter" ne "" %then %do; 
       %if %index(%lowcase(&filename),%lowcase(&iFilter)) %then %do; 
       %let result = &result%str(&iDelimiter)&filename; 
       %end; 
      %end; 
      %else %do; 
       %let result = &result%str(&iDelimiter)&filename; 
      %end; 
      %end; 
     %end; 
     %else %do; 
      %if "&iFilter" ne "" %then %do; 
      %if %index(%lowcase(&filename),%lowcase(&iFilter)) %then %do; 
       %let result = &result%str(&iDelimiter)&filename; 
      %end; 
      %end; 
      %else %do; 
      %let result = &result%str(&iDelimiter)&filename; 
      %end; 
     %end; 
     %end; 
     %else %do; 
     %put ERROR: (CMN_MAC.FILE_LIST) FILE CANNOT BE READ.; 
     %put %sysfunc(sysmsg()); 
     %end; 
    %end; 

    %end; 
    %else %do; 
    %put ERROR: (CMN_MAC.FILE_LIST) PATH DOES NOT EXIST OR CANNOT BE OPENED.; 
    %put %sysfunc(sysmsg()); 
    %end; 

    /* 
    ** RETURN THE RESULT. TRIM THE LEADING DELIMITER OFF THE FRONT OF THE RESULTS. 
    */ 
    %if "&result" ne "" %then %do; 
    %substr(&result,2) 
    %end; 

%mend; 



** 
** EXAMPLES - HAVENT TESTED THE LAST TWO YET BUT THEY SHOULD WORK IF SYNTAX IS CORRECT 
*; 

%put %file_list(iPath=c:\temp); 

%put %file_list(iPath=c:\xxdffsds); 

%put %file_list(iPath=c:\rob\SASDev\, iFilter=a); 

%put %file_list(iPath=c:\rob\SASDev\,iFiles_only=1); 

%put %file_list(iPath=/tmp/unix_sasdir,iFiles_only=1); 

data x; 
    file_list = "%file_list(iPath=c:\temp)"; 
run; 

proc sql noprint; 
    insert into my_table values ("%file_list(iPath=c:\temp,iDelimiter=%str(","))"); 
quit; 
Problemi correlati