Uso de SAS Macro para canalizar una lista de nombres de archivos desde un directorio de Windows

StackOverflow https://stackoverflow.com/questions/1409543

Pregunta

Estoy tratando de enmendar la macro a continuación para aceptar un parámetro de macro como el argumento 'ubicación' para un comando dir. Sin embargo, no puedo hacer que se resuelva correctamente debido al problema de las citas anidadas. El uso de% str (% ') no funciona, ni las funciones de cita por alguna razón.

La macro funcionará bien cuando la ruta de archivo tenga sin espacios (por ejemplo, C: \ temp \ withnospace) ya que las comillas intermedias no son necesarias. Sin embargo, necesito que esta macro funcione para las rutas de archivo con espacios (por ejemplo, 'C: \ temp \ with space \').

¡Por favor ayuda!

%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 */
¿Fue útil?

Solución

Aquí hay otra forma de lograr el mismo resultado sin necesidad de utilizar 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));

Otros consejos

Realice los siguientes cambios y su código funcionará.

%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) Finalice la declaración % macro con un punto y coma;

(2) Rodee la resolución de la variable macro con comillas dobles duplicadas y % unquote ;

(3) Libere el identificador de archivo al borrarlo; y

(4) No cites solo tu parámetro de entrada. cita macro en su lugar, si es necesario.

Basado en la última muestra en esto página , en lugar de la declaración del nombre de archivo, intente

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

y llame a la macro sin usar comillas:

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

También probé las citas de macro, pero no pude hacer que funcionara.

aquí hay una macro rápida para agrupar listas de directorios basadas en Windows en un conjunto de datos 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;

y así es como se llaman

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

observe que no hay una barra diagonal inversa al especificar el directorio base. C: no es C: \ .

funciona para mí si llamo a la macro original de esta manera

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

por supuesto que tuve que agregar el punto y coma al final de la declaración % macro .

Si su directorio contiene una coma, suceden cosas malas. para corregirlo, use la macro % str ()

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

Aquí hay una versión de código de macro pura. También le permite especificar que solo desea conocer los archivos (y no las carpetas) y le permite especificar un filtro básico. Devuelve la lista de archivos en un formato delimitado, pero puede insertarlos fácilmente en un conjunto de datos utilizando la inserción de SQL si lo desea (ejemplo incluido pero no probado, sin cajero automático de acceso SAS). Se puede llamar desde cualquier lugar, dentro de otra macro, un conjunto de datos, una declaración SQL ... donde sea. Simplemente agregue estas dos macros a su biblioteca de llamadas automáticas de macros y tendrá razón en ir.

Hay 2 macros abajo. La macro% file_list requiere la macro% isdir. Las macros son un poco más grandes y más complejas que las anteriores, pero son MUCHO más flexibles. Además, proporcionan verificación de errores.

/******************************************************************************
** 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;

Aquí hay uno que descifra el orden de citar y no citar:

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

filename datain pipe &command;

donde la variable de macro basedir puede contener espacios y también los nombres de archivo. Esta combinacion de % unquote y % str (% ') es un lenguaje macro que ocurre con frecuencia.

" ¿qué pasa si tengo comillas simples en mi directorio? "

El manejo de esta situación requiere una función de cita de macros, como % bquote (); Continuando con el ejemplo anterior, esto:

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

debería hacerlo.

Para evitar infinitas iteraciones de este tipo de pregunta, mire El artículo de Ian Whitlock, Una mirada seria a las citas macro, que está disponible aquí ;

Hay (muchos) otros, pero este es el más citado. Una pequeña nota: cualquier cosa de Ian Whitlock probablemente valga la pena. Escribe claramente y su comprensión de los problemas de SAS es impresionante.

Usamos esta pequeña 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;

Llamada de ejemplo

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

Si se ejecuta en lotes y no tiene la opción noxwait xsync , el trabajo se bloqueará en el servidor a la espera de una respuesta del operador.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top