Использование макроса SAS для передачи списка имен файлов из каталога Windows
Вопрос
Я пытаюсь изменить приведенный ниже макрос, чтобы он принимал параметр макроса в качестве аргумента 'location' для команды dir. Однако я не могу заставить его решить правильно из-за проблемы вложенных кавычек. Использование% str (% ') не работает, равно как и функции цитирования по некоторым причинам.
Макрос будет работать нормально, если в пути к файлу нет пробелов (например, C: \ temp \ withnospace), поскольку средние кавычки не нужны. Однако мне нужно, чтобы этот макрос работал для файловых путей с пробелами (например, 'C: \ temp \ with space \').
Пожалуйста, помогите! Р>
%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 */
Решение
Вот еще один способ достижения того же результата без использования ТРУБЫ. Р>
%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));
Другие советы
Внесите следующие изменения, и ваш код будет работать.
%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) Завершить оператор % macro
точкой с запятой;
(2) Окружите разрешение макропеременной двойными двойными кавычками и % unquote
;
(3) Отпустите дескриптор файла, очистив его; и р>
(4) Не заключайте в кавычки ваш входной параметр. вместо этого макрос, если необходимо.
На основании последнего примера на этом страница вместо оператора имени файла попробуйте
%let filrf=pipedir;
%let rc=%sysfunc(filename(filrf,%bquote(dir "&location" /b),pipe));
и вызов макроса без кавычек:
%get_filenames(c:\temp\with spaces);
Я также пытался использовать цитаты макросов, но не смог заставить его работать.
вот быстрый макрос для извлечения списков каталогов на основе Windows в набор данных 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;
и вот как их называют
%dirlist(c:); %dirlist(c:\temp);
обратите внимание, что при указании базового каталога нет обратной косой черты. C:
не C: \
.
это работает для меня, если я так называю оригинальный макрос
%get_filenames(""C:\Program Files"")
Конечно, мне пришлось добавить точку с запятой в конце оператора % macro
.
если ваш каталог содержит запятую, случается что-то плохое. чтобы исправить это, используйте макрос % str ()
%get_filenames(%str(C:\temp\comma, fail))
Вот версия кода чистого макроса. Он также позволяет вам указать, что вы хотите знать только о файлах (а не о папках), и позволяет указать базовый фильтр. Он возвращает список файлов в формате с разделителями, но вы можете легко вставить их в набор данных, используя SQL-вставку, если хотите (пример включен, но не проверен - нет доступа к SAS). Его можно вызывать из любого места - внутри другого макроса, набора данных, оператора SQL ... где угодно. Просто добавьте эти два макроса в вашу библиотеку макросов, и все готово.
Ниже приведены 2 макроса. Макрос% isdir требуется для макроса% file_list. Макросы немного больше и сложнее, чем выше, но они НАМНОГО более гибкие. Кроме того, они обеспечивают проверку ошибок.
/******************************************************************************
** 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;
Вот тот, который расшифровывает порядок цитирования и удаления цитат:
%let command =%unquote(%str(%')dir "&baseDir.data\*txt"%str(%'));
filename datain pipe &command;
где макропеременная basedir может содержать пробелы и имена файлов.
Это сочетание
% unquote
и % str (% ')
часто встречаются в макросах.
" что если в моем каталоге есть одинарные кавычки? "
Для обработки этой ситуации требуется функция макросов, такая как % bquote ();
Продолжая пример выше, это:
%let command =%unquote(%str(%')dir "%bquote(&baseDir.data\*txt)"%str(%'));
должен это сделать.
Чтобы избежать бесконечных итераций такого рода вопроса, посмотрите на Статья Иана Уитлока «Серьезный взгляд на цитирование макросов», которую можно найти здесь ; Р>
Есть (много) другие, но это наиболее часто цитируемые. Небольшое примечание: что-нибудь от Ian Whitlock, вероятно, стоит. Он пишет ясно, и его понимание проблем SAS удивительно.
Мы используем этот маленький макрос
%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;
Пример звонка
%getdir(dir=c:\temp\,redirect=c:\temp\dir.txt) *run;
Если вы запускаете пакетно и у вас нет опции noxwait xsync
, задание будет зависать на сервере в ожидании ответа оператора.