2009-09-11 31 views
7

Tôi đang cố sửa đổi macro dưới đây để chấp nhận thông số macro làm đối số 'vị trí' cho lệnh dir. Tuy nhiên tôi không thể giải quyết nó một cách chính xác do vấn đề trích dẫn lồng nhau. Sử dụng% str (% ') không hoạt động, không trích dẫn các hàm cho một số lý do.Sử dụng SAS Macro để tạo danh sách tên tệp từ thư mục Windows

Macro sẽ hoạt động tốt khi tệp filepath có không có dấu cách (ví dụ: C: \ temp \ withnospace) vì không cần dấu ngoặc kép ở giữa. Tuy nhiên, tôi cần macro này để làm việc cho các tập tin đính kèm với các không gian (ví dụ: 'C: \ temp \ with space \').

Vui lòng trợ giúp!

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

Trả lời

13

Đây là một cách khác để đạt được cùng một kết quả mà không cần sử dụng 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

mặc dù điều này không thực sự trả lời câu hỏi, mã này vượt trội hơn nhiều so với nó có thể được sử dụng trên các môi trường - và hoàn thành cùng một mục đích. thankyou ... –

1

Chúng tôi sử dụng này ít vĩ mô

%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; 

Mẫu Gọi

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

Nếu bạn chạy trong hàng loạt và không có option noxwait xsync công việc sẽ treo trên máy chủ chờ đợi một phản ứng của nhà điều hành.

3

Dựa trên mẫu cuối cùng trên this page, thay vì báo cáo kết quả filename, hãy thử

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

và gọi vĩ mô mà không sử dụng dấu ngoặc kép:

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

Tôi cũng đã cố gắng vĩ mô trích dẫn, nhưng couldn' Làm cho nó hoạt động.

+0

+1 - đẹp. Bạn cũng có thể chuyển lrecl như một tùy chọn máy chủ để nó giống như bản gốc: % cho rc =% sysfunc (tên tệp (filrf,% bquote (dir "& location"/b), pipe, lrecl = 32767)); – cmjohns

2

nó làm việc cho tôi nếu tôi gọi vĩ mô ban đầu theo cách này

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

tất nhiên tôi đã phải thêm dấu chấm phẩy ở cuối câu lệnh %macro.

nếu thư mục của bạn chứa dấu phẩy, điều xấu sẽ xảy ra. để khắc phục, hãy sử dụng macro %str()

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

6

Thực hiện một số thay đổi sau và mã của bạn sẽ hoạt động.

%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) Kết thúc câu hỏi %macro với dấu chấm phẩy;

(2) Bao quanh độ phân giải biến vĩ mô với dấu ngoặc kép tăng gấp đôi và %unquote;

(3) Thả tay cầm tệp lên bằng cách xóa nó; và

(4) Không trích dẫn thông số đầu vào của bạn. thay vào đó, nếu cần.

+0

tác phẩm này - nhưng để lại một thông báo lỗi (CẢNH BÁO: Không gán logic cho tên tệp PIPE.) –

+2

chỉ thay đổi tên tệp rõ ràng; đến tên tệp pipedir rõ ràng; để ngăn chặn cảnh báo. – cmjohns

3

đây là macro nhanh để kéo danh sách thư mục dựa trên cửa sổ vào bộ dữ liệu 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; 

và dưới đây là cách họ được gọi

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

thông báo không có dấu chéo ngược dấu khi xác định thư mục gốc. C: không phải C:\.

+0

Tôi thích điều này nhưng tôi phải thực hiện một vài thay đổi để nó hoạt động trên hệ thống của tôi (win xp). Tôi đã thay đổi re_subd = line thành re_subd = prxparse ("/ (\ d \ d \/\ d \ d \/\ d \ d \ d \ d) \ s + (\ d \ d: \ d \ d [A | P] M) \ s +

\ s + (\ S. *)/"); vì mỗi dòng được coi là một thư mục con cho tôi. Ngoài ra, tôi đã xóa% if & SUBDIR eq% rồi% let subdir =/s; dòng từ & biến macro macro không được xác định và không được sử dụng ở nơi khác. – cmjohns

+0

tôi nhớ phải thực hiện thay đổi đối với mẫu regex re_subd khi hệ điều hành được nâng cấp, nhưng không bao giờ nghĩ đến việc lưu phiên bản trước đó. thông số SUBDIR được dùng để chỉ định chuyển đổi/s cho danh sách thư mục con đệ quy. một ngày nào đó. – rkoopmann

2

Đây là một loại unscrambles thứ tự trích dẫn và unquoting:

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

filename datain pipe &command; 

nơi vĩ mô biến basedir có thể chứa khoảng trắng và do đó có thể tên file. Sự kết hợp này của %unquote%str(%') là thành ngữ macro thường xuyên xảy ra.

"điều gì sẽ xảy ra nếu tôi có một trích dẫn trong thư mục của mình?"

Xử lý tình trạng này đòi hỏi một macro chức năng trích dẫn, chẳng hạn như %bquote(); Tiếp tục ví dụ ở trên, điều này:

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

nên làm điều đó.

Để tránh các lần lặp vô hạn của loại câu hỏi này, hãy xem Bài viết của Ian Whitlock, Một cái nhìn nghiêm túc về việc trích dẫn macro, có sẵn here;

Có nhiều (nhiều) người khác, nhưng đây là trích dẫn được nhiều người biết đến nhất. Một lưu ý nhỏ: bất cứ điều gì bởi Ian Whitlock có lẽ là đáng giá. Ông viết rõ ràng và sự hiểu biết của ông về các vấn đề SAS là tuyệt vời.

2

Đây là phiên bản mã macro thuần túy. Nó cũng cho phép bạn chỉ định rằng bạn chỉ muốn biết về các tệp (và không phải thư mục) và cho phép bạn chỉ định một bộ lọc cơ bản. Nó trả về danh sách các tập tin trong một định dạng phân định nhưng bạn có thể dễ dàng chèn chúng vào một tập dữ liệu bằng cách sử dụng chèn SQL nếu bạn muốn (ví dụ bao gồm nhưng không được thử nghiệm - không có truy cập SAS atm). Nó có thể được gọi từ bất cứ đâu - bên trong một macro khác, một tập dữ liệu, một câu lệnh sql ... bất cứ nơi đâu. Chỉ cần thêm hai macro này vào thư viện autocall macro của bạn và bạn đã sẵn sàng để đi.

Có 2 macro bên dưới. Macro% isdir được yêu cầu bởi macro% file_list. Các macro là lớn hơn một chút và phức tạp hơn so với ở trên nhưng chúng là MUCH linh hoạt hơn. Thêm vào đó họ cung cấp kiểm tra lỗi.

/****************************************************************************** 
** 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; 
Các vấn đề liên quan