Utilisation de la macro SAS pour diriger une liste de noms de fichiers à partir d'un répertoire Windows

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

Question

J'essaie de modifier la macro ci-dessous pour accepter un paramètre de macro en tant qu'argument 'location' pour une commande dir. Cependant, je ne parviens pas à le résoudre correctement en raison du problème des guillemets imbriqués. Utiliser% str (% ') ne fonctionne pas, pas plus que citer des fonctions pour une raison quelconque.

La macro fonctionnera correctement si le chemin du fichier ne contient pas d'espaces (par exemple, C: \ temp \ withnospace), car les guillemets centraux ne sont pas nécessaires. Cependant, j'ai besoin de cette macro pour les chemins de fichiers avec des espaces (par exemple, 'C: \ temp \ avec espace \').

S'il vous plaît aider!

%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 */
Était-ce utile?

La solution

Voici un autre moyen d’obtenir le même résultat sans avoir à utiliser 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));

Autres conseils

Effectuez les modifications suivantes et votre code fonctionnera.

%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) Terminez l'instruction % macro par un point-virgule;

(2) Entourez la résolution de la variable macro avec des guillemets doubles et % unquote ;

.

(3) Libérez le descripteur de fichier en le supprimant. et

(4) Ne citez pas un seul paramètre avec votre paramètre d’entrée. devis macro à la place, si nécessaire.

Basé sur le dernier exemple de ceci page , au lieu de l'instruction filename, essayez

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

et appelez la macro sans utiliser de guillemets:

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

J'ai aussi essayé de citer les macros, mais je n'ai pas réussi à le faire fonctionner.

Voici une macro rapide pour extraire les listes de répertoires Windows dans un jeu de données 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;

et voici comment ils s'appellent

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

remarquez qu'il n'y a pas de barre oblique inverse lors de la spécification du répertoire de base. C: pas C: \ .

cela fonctionne pour moi si j'appelle la macro d'origine de cette façon

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

bien sûr, je devais ajouter le point-virgule à la fin de l'instruction % macro .

Si votre répertoire contient une virgule, de mauvaises choses se produisent. pour résoudre ce problème, utilisez la macro % str ()

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

Voici une version de code macro pur. Il vous permet également de spécifier que vous souhaitez uniquement connaître les fichiers (et non les dossiers) et vous permet de spécifier un filtre de base. Il renvoie la liste des fichiers dans un format délimité, mais vous pouvez facilement les insérer dans un jeu de données à l'aide de l'insertion SQL si vous le souhaitez (exemple inclus mais non testé - pas d'accès SAS). Il peut être appelé de n'importe où - dans une autre macro, un jeu de données, une instruction SQL ... n'importe où. Ajoutez simplement ces deux macros à votre bibliothèque d’appels automatiques de macros et vous avez raison de partir.

Il y a 2 macros ci-dessous. La macro% isdir est requise par la macro% file_list. Les macros sont un peu plus grandes et complexes que celles ci-dessus, mais elles sont BEAUCOUP plus flexibles. De plus, ils fournissent une vérification d'erreur.

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

En voici un qui déchiffre l'ordre des guillemets et des guillemets:

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

filename datain pipe &command;

où la variable de macro basedir peut contenir des espaces, de même que les noms de fichiers. Cette combinaison de % unquote et % str (% ') est un langage de macro fréquemment utilisé.

"Et si j'ai une seule citation dans mon répertoire?"

La gestion de cette situation nécessite une fonction de citation de macro, telle que % bquote (); En reprenant l’exemple ci-dessus, voici:

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

devrait le faire.

Pour éviter les itérations infinies de ce genre de question, regardez L'article de Ian Whitlock intitulé Regard sérieux sur les citations de macros est disponible ici ;

Il y en a (beaucoup) d’autres, mais c’est le plus cité. Un petit mot: tout ce que Ian Whitlock a valu en vaut probablement la peine. Il écrit clairement et sa compréhension des problèmes liés à SAS est extraordinaire.

Nous utilisons cette petite 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;

Exemple d'appel

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

Si vous exécutez en batch sans l'option noxwait xsync , le travail sera bloqué sur le serveur en attente d'une réponse de l'opérateur.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top