TOPIC: FILENAME
Safe file copying methods for cross platform SAS programming
Not so long ago, I needed to copy a file within a SAS program, only to find that an X command would not accomplish the operation. That cast my mind back to file operations using SAS in order to be platform-independent. Thus, I fell upon statements within a data step.
Before going that far, you need to define filename statements for the source and target locations like this:
filename src "<location of source file or files>" lrecl=32767 encoding="utf-8";
filename dst "<target location for copies>" lrecl=32767 encoding="utf-8";
Above, I also specified logical record length (LRECL) and UTF-8 encoding. The latter is safer in these globalised times, when the ASCII character set does not suffice for everyday needs.
Once you have defined filename statements, you can move to the data step itself, which does not output any data set because of the data _null_ statement:
data _null_;
length rc msg $ 300;
rc = fcopy('src','dst');
if rc ne 0 then do;
msg = sysmsg();
putlog "ERROR: FCOPY failed rc=" rc " " msg;
end;
else putlog "NOTE: Copied OK.";
run;
The main engine here is the fcopy function, which outputs a return code (rc). Other statements like putlog are there to communication outcomes and error messages when the file copying operation fails. The text of the error message (msg) comes from the sysmsg function. After file copying has completed, it is time to unset the filename statements as follows:
filename src clear;
filename dst clear;
One thing that needs to be pointed out here is that this is an approach best reserved for text files like what I was copying when doing this. When I attempted the same kind of operation with an XLSX file, the copy would not open in Excel afterwards. Along the way, it had got scrambled. Once you realise that an XLSX file is essentially a zip archive of XML files, you might see how that could go awry.
Something to try when you get a message like this caused by a filename with a leading hyphen: "mv: illegal option -- u"
Recently, I downloaded some WEBP files from Ideogram and attempted to move them to another folder. That is when I got the message that you see in the title of this entry. Because I had not looked at the filenames, I baffled when I got this from a simple command that I had been using with some success until then. Because I was using an iMac, I tried the suggestion of installing coreutils to get GNU mv and cp to see if that would help:
brew install coreutils
The above command gave me gmv and gcp for the GNU versions of mv and cp that comes with macOS. Trying gmv only got me the following message:
gmv: cannot combine --backup with --exchange, -n, or --update=none-fail
The ls command could list all files, but not the WEBP ones. Thus, I executed the following to show what I wanted:
ls | grep -i webp
That got around the problem by doing a subset of the directory listing. It was then that I spotted the leading hyphen. To avoid the problem tripping me up again, I renamed the offending file using this command:
mv -- -iunS9U4RFevWpaju6ArIQ.webp iunS9U4RFevWpaju6ArIQ.webp
Here, the -- switch tells the mv command not to look for any more options and only to expect filenames. When I tried enclosing the filename in quotes, I still got problems, even that might have because I was using double quotes instead of single quotes. Another option is to escape the leading hyphen like this:
mv ./-iunS9U4RFevWpaju6ArIQ.webp iunS9U4RFevWpaju6ArIQ.webp
Once the offending file was renamed, I could move the files to their final location. That could have used the -- option too, saving me an extra command, only for my wanting this not to trip me up again. Naturally, working in Finder might have avoided all this as much as not having a file with a leading hyphen in its name, but there would have been nothing to learn then.
Generating PNG files in SAS using ODS Graphics
Recently, I had someone ask me how to create PNG files in SAS using ODS Graphics, so I sought out the answer for them. Normally, the suggestion would have been to create RTF or PDF files instead, but there was a specific need that needed a different approach. Adding something like the following lines before an SGPLOT, SGPANEL or SGRENDER procedure should do the needful:
ods listing gpath='E:\';
ods graphics / imagename="test" imagefmt=png;
Here, the ODS LISTING statement declares the destination for the desired graphics file, while the ODS GRAPHICS statement defines the file name and type. In the above example, the file test.png would be created in the root of the E drive of a Windows machine. However, this also works with Linux or UNIX directory paths.
Using SAS FILENAME statement to extract directory file listings into SAS
The filename statement's pipe option allows you to direct the output of operating system commands into SAS for further processing. Usefully, the Windows dir command (with its /s switch) and the UNIX and Linux equivalent ls allow you to get a file listing into SAS. For example, here's how you extract the list of files in your UNIX or Linux home directory into SAS:
filename dirlist pipe 'ls ~';
data dirlist;
length filename $200;
infile dirlist length=reclen;
input buffer $varying200. reclen;
run;
Using the ftp option on the filename statement allows you to get a list of the files in a directory on a remote server, even one with a different operating system to that used on the client (PC or server), very useful for cases where cross-platform systems are involved. Here's some example code:
filename dirlist ftp ' ' ls user='user' host='host' prompt;
data _null_;
length filename $200;
infile dirlist length=reclen;
input buffer $varying200. reclen;
run;
The PROMPT option will cause SAS to ask you for a password, and the null string is where you would otherwise specify the name of a file.
Checking existence of files and directories on UNIX using shell scripting
Having had a UNIX shell script attempt to copy a non-existent file, I decided to take another look for ways to test the existence of a file. For directory existence checking, I was testing for the return code from the cd command, and I suppose that the ls command might help for files. However, I did find a better way:
if [ -f $filename ]
then
echo "This filename [$filename] exists"
elif [ -d $dirname ]
then
echo "This dirname [$dirname] exists"
else
echo "Neither [$dirname] or [$filename] exist"
fi
The -d and -f flags within the evaluation expressions test for the existence of directories and files, respectively. One gotcha is that those spaces within the brackets are important too, but it is a very way of doing what I wanted.