I've the code as below to move the files from source to target. When I tried to execute the code below, I've got error as,
ERROR: Unable to copy. source=/var/sasdata/INPUT/GT/IFR_GH_MSTD_0069.xlsx target=/var/sasdata/INPUT/LH/IFR_GH_MSTD_0069.xlsx rc3=-7440230 msg=WARNING: 3 records were truncated when the FCOPY function read from fileref FROM. 0 records were truncated when the FCOPY functi on wrote to fileref TO. To prevent the truncation of records in future operations, you can increase the amount of space needed t NOTE: There were 2 observations read from the data set WORK.FILES_TARGET_CHECK.
What would be likely cause for thsi error? After the execution of the program I want both .xlsx and .csv file to move to target folder but only .csv has moved. Program which I used is,
data files_moved; set files_target_check ; length rc1-rc4 8 msg $256 ; /*Copy and delete*/ if not (scan(lowcase(filename),5,'_') in ('5601','6010','6020')) or index(lowcase(filename),'_copied.csv ') then do; rc1=filename('from',source); rc2=filename('to',target); rc3=fcopy('from','to'); if rc3 then do; msg=sysmsg(); put 'ERROR: Unable to copy. ' source= target= rc3= msg=; end; else do; rc4=fdelete('from'); end; output; end; rc1=filename('from'); rc2=filename('to'); run;
Data from files_target_check is,
filename | source | target | flag |
IFR_GH_MSTD_0069.xlsx | /var/sasdata/INPUT/GT/IFR_GH_MSTD_0069.xlsx | /var/sasdata/INPUT/LH/IFR_GH_MSTD_0069.xlsx | 1 |
IFR_GH_TRND_1_0069_1_20201009T075212.csv | /var/sasdata/INPUT/GT/IFR_GH_TRND_1_0069_1_20201009T075212.csv | /var/sasdata/INPUT/LH/IFR_GH_TRND_1_0069_1_20201009T075212.csv | 1 |
I suspect the "what" is that the xlsx file format is actually 1) a zip file and 2) multiple compressed XML files.
The "why" might be the use of the wrong function. From the documentation:
Fcopy copies records, which in a simple text file such as CSV, are lines of data. XML is not like that (open an XML in a text editor and see the difference) so I am not sure what Fcopy may see as a "record" in such.
@Peter_C @Tom can't we move .xlsx and .csv file in one single data step as shown below?
data files_moved; set files_target_check ; length rc1-rc4 8 msg $256 ; /*Copy and delete*/ if not (scan(lowcase(filename),5,'_') in ('5601','6010','6020')) or index(lowcase(filename),'_copied.csv ') then do; rc1=filename('from',source); rc2=filename('to',target); rc3=fcopy('from','to'); if rc3 then do; msg=sysmsg(); put 'ERROR: Unable to copy. ' source= target= rc3= msg=; end; else do; rc4=fdelete('from'); end; output; end; rc1=filename('from'); rc2=filename('to'); run;
Maxim 1: Read the Documentation.
Example 2 of the FCOPY Function shows how to copy a binary file. Important: use RECFM=N for the file reference.
Edit: changed the RECFM to N, as it is in the documentation (typo, my bad)
@Kurt_Bremser Is it recfm=n or recfm=f? How to use RECFM in my program below as I do not have filename statement (used only filename function) due to the fact that filename is dynamic. Sometimes I receive .xlsx and sometime .csv otherewise both CSV and Excel.
data files_moved; set files_target_check ; length rc1-rc4 8 msg $256 ; /*Copy and delete*/ if not (scan(lowcase(filename),5,'_') in ('5601','6010','6020')) or index(lowcase(filename),'_copied.csv ') then do; rc1=filename('from',source); rc2=filename('to',target); rc3=fcopy('from','to'); if rc3 then do; msg=sysmsg(); put 'ERROR: Unable to copy. ' source= target= rc3= msg=; end; else do; rc4=fdelete('from'); end; output; end; rc1=filename('from'); rc2=filename('to'); run;
Hi, for copying files I generally use the byte-for-byte technique including setting the chunksize to speed up the copying process
%*-------------------------------------------------------------------------;
%* Set source and target (copy) path and file name values: ;
%*-------------------------------------------------------------------------;
%let infile = %str();
%let outfile = %str();
%*-------------------------------------------------------------------------;
%* Set copy mode and chunksize variables: ;
%*-------------------------------------------------------------------------;
%let mode = S ; /* I (chunksize is 1) / S (use chunksize value) */
%let chunksize = 32767 ; /* 1 - 32767 */
%*-------------------------------------------------------------------------;
%* Copy INFILE to OUTFILE using the byte-for-byte method: ;
%*-------------------------------------------------------------------------;
filename in "&infile.";
filename out "&outfile.";
data _null_;
length
ifile 8
ofile 8
fmtlen 8
bytes 8
outfmt $ 32
rec $ &chunksize.
;
ifile = fopen('in',"&mode.",&chunksize.,'B');
ofile = fopen('out','O',&chunksize.,'B');
bytes = 0;
rec = '20'x;
do while(fread(ifile) eq 0);
call missing(outfmt, rec);
rc = fget(ifile,rec,&chunksize.);
if "%upcase(&mode.)" eq "I" then do;
fmtlen = 1;
outfmt = 1;
end;
else do;
fcolin = fcol(ifile);
if (fcolin - &chunksize.) eq 1 then do;
fmtlen = &chunksize.;
end;
else do;
fmtlen = fcolin - 1;
end;
outfmt = cats("$char", fmtlen, ".");
end;
bytes + fmtlen;
rc = fput(ofile,putc(rec,outfmt));
rc = fwrite(ofile);
end;
call symputx('size', bytes);
rc = fclose(ifile);
rc = fclose(ofile);
run;
filename in clear;
filename out clear;
%put File %scan(&infile.,-1,/\) copied to %scan(&outfile.,-1,/\).;
%put Transferred &size. Bytes.;
Best regards,
Paul
Sorry about the confusion, the RECFM has to be N (when there's a contradiction between me and the documentation, the documentation is most probably correct).
You need to supply the options in a separate parameter of the FILENAME function, and you can copy text files as binary without problems:
proc export
data=sashelp.class
file="/folders/myfolders/class.xlsx"
dbms=xlsx
replace
;
run;
proc export
data=sashelp.class
file="/folders/myfolders/class.csv"
dbms=csv
replace
;
run;
data _null_;
rc = filename("from","/folders/myfolders/class.xlsx","","recfm=n");
put rc=;
rc = filename("to","/folders/myfolders/test/class.xlsx","","recfm=n");
put rc=;
rc = fcopy("from","to");
put rc=;
rc = filename("from","/folders/myfolders/class.csv","","recfm=n");
put rc=;
rc = filename("to","/folders/myfolders/test/class.csv","","recfm=n");
put rc=;
rc = fcopy("from","to");
put rc=;
run;
proc import
datafile="/folders/myfolders/test/class.xlsx"
dbms=xlsx
out=work.class1
replace
;
run;
proc import
datafile="/folders/myfolders/test/class.csv"
dbms=csv
out=work.class2
replace
;
run;
This code runs without ERRORs or WARNINGs, and creates two (nearly) identical datasets in WORK.
The values are identical, but some attributes differ (formats, informats) because of PROC IMPORT.
@Kurt_Bremser what will happen if I use recfm=n for CSV files as I shown below? Because in real life I may transfer CSV or Excel file or both as files are dynamic.
data _null_; rc = filename("from","/folders/myfolders/class.csv","","recfm=n");
run;
The answer is in my previous post. Please read the code closely, you will find that I used the "binary" method for a csv file without problems.
For reference we recently published a macro that will (binary) copy ALL files in a particular directory, and recursively so: https://core.sasjs.io/mp__copyfolder_8sas.html
It's finally time to hack! Remember to visit the SAS Hacker's Hub regularly for news and updates.
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.
Ready to level-up your skills? Choose your own adventure.