SAS: macro send email if exist - email

I try build little system which will verify that attachment exist (from 4 sources) and if exists - send this attachments with email.
If exist one - then send it.
I found interesting code but I have problem with little verify four files and gluing variable with path to them.
%LET ATTACH1=%STR(C:\XXX\YYYY\ZZZZZ\XYZ_1_&calosc..csv);
%LET ATTACH2=%STR(C:\XXX\YYYY\ZZZZZ\XYZ_2_&calosc..csv);
%LET ATTACH3=%STR(C:\XXX\YYYY\ZZZZZ\XYZ_3_&calosc..csv);
%LET ATTACH4=%STR(C:\XXX\YYYY\ZZZZZ\XYZ_4_&calosc..csv);
%MACRO FINDMYFILE;
%LET ZALACZNIKI = ;
%IF %SYSFUNC(FILEEXIST(&ATTACH1)) %THEN &ZALACZNIKI = &ZALACZNIKI || &ATTACH1 || ' ';
%IF %SYSFUNC(FILEEXIST(&ATTACH2)) %THEN &ZALACZNIKI = &ZALACZNIKI || &ATTACH2 || ' ';
%IF %SYSFUNC(FILEEXIST(&ATTACH3)) %THEN &ZALACZNIKI = &ZALACZNIKI || &ATTACH3 || ' ';
%IF %SYSFUNC(FILEEXIST(&ATTACH4)) %THEN &ZALACZNIKI = &ZALACZNIKI || &ATTACH4 || ' ';
%PUT &ZALACZNIKI;
%IF %SYSFUNC(FILEEXIST(&ATTACH1)) OR %SYSFUNC(FILEEXIST(&ATTACH2)) OR %SYSFUNC(FILEEXIST(&ATTACH3)) OR %SYSFUNC(FILEEXIST(&ATTACH4)) %THEN
%DO;
FILENAME OUTBOX EMAIL
FROM = ("XX SYSTEM REPORT <noreply#systemaccount>")
TO = ("xzy.yzx#email.com")
CC = ("xzy.yzx#email.com")
REPLYTO = ("xzy.yzx#email.com")
SUBJECT = (" testowy ")
ATTACH = ("&ZALACZNIKI.");
DATA _NULL_;
FILE OUTBOX;
PUT "Hello,";
PUT ;
PUT %SYSFUNC(COMPBL(
"This is an example email."));
PUT ;
PUT %SYSFUNC(COMPBL(
"By using COMPBL we remove extra blanks from our text."));
PUT %SYSFUNC(COMPBL(
"There is no separation with this email line."));
RUN;
FILENAME OUTBOX CLEAR;
%END;
%ELSE %PUT NOTE: FILE DOES NOT EXIST AND NO EMAIL WILL BE SENT.;
%MEND FINDMYFILE;
%FINDMYFILE;
Thank for help :)

You're missing a few things here:
%IF %SYSFUNC(FILEEXIST(&ATTACH4)) %THEN &ZALACZNIKI = &ZALACZNIKI || &ATTACH4 || ' ';
You need to use %LET to reassign a macro variable, if that's your intention, and the concatenation operators are not required, but you may need double-quotes around the filename:
%IF %SYSFUNC(FILEEXIST(&ATTACH4)) %THEN %LET ZALACZNIKI = &ZALACZNIKI. "&ATTACH4.";
When you come to use the macro variable, you probably won't need the quotes, otherwise you'll get:
""file1" "file2" "file3""
So just do this:
ATTACH = (&ZALACZNIKI.);
Experiment with this code to learn how it works:
%let file1 = C:\temp;
%let file2 = C:\temp2;
%let z = ;
%let z = &z. "&file1.";
%let z = &z. "&file2.";
%put &z;

Related

How do I use BOF function in QBASIC?

I came to know that there is a BOF function available to use in QBASIC. It is called the Beginning Of File. But, I didn't find any examples over its use. Please help.
Here is an example for a possible BOF function:
' example BOF function in QB
' returns beginning of file
PRINT "Enter filename";: INPUT F$
Handle = FREEFILE
OPEN F$ FOR BINARY AS #Handle
PRINT "BOF="; BOF(Handle)
END
' function to get BOF
FUNCTION BOF (H)
IF LOF(H) > 0 THEN
BOF = 1
ELSE
BOF = 0
END IF
END FUNCTION
Sample to determine if file is at BOF:
' example BOF function in QB
' returns true if at beginning of file.
PRINT "Enter filename";: INPUT F$
Handle = FREEFILE
OPEN F$ FOR BINARY AS #Handle
IF BOF(Handle) THEN
PRINT "File is at BOF"
END IF
END
' function to get BOF
FUNCTION BOF (H)
IF LOC(H) <= 1 THEN
BOF = -1
ELSE
BOF = 0
END IF
END FUNCTION

Nested loop in SAS macro

I got error when I was trying to use macro nested loop in SAS:
%Let t1=30;
%Let t2=40;
%Let t3=50;
%Let t4=60;
%Let t5=70;
%macro Age(time);
%Do I = 1 %to &time.;
data Milk&I;
set Milk;
/*If Age is less than 30, MilkF and MilkA after 30 should be 0, same for 40-70*/
where (age<&&t&I. and (%Do I = &I. %to 5;
MilkF&&t&I. ne 0 or MilkA&&t&I. ne 0 ;
%end;) ) ;
run;
%end;
%mend Age;
%Age(5)
The error shows behind the last "ne 0;" Syntax Error.
What's the problem? Thanks for your help!
UPDATE:
The output from macro I want is (takeing t1=30 as an example):
where (age<30 and (
MilkF30 ne 0 or MilkA30 ne 0 or
MilkF40 ne 0 or MilkA40 ne 0 or
MilkF50 ne 0 or MilkA50 ne 0 or
MilkF60 ne 0 or MilkA60 ne 0 or
MilkF70 ne 0 or MilkA70 ne 0
) ) ;
So I have changed my code to
where (age<&&t&I. and
(%Do I = &I. %to 5;
MilkFreq&&t&I. ne 0 or MilkAmnt&&t&I. ne 0 or
%end;
) ) ;
Error:
) ) ; run;
-
22
76
"ERROR: Syntax error while parsing WHERE clause."
So what happened now?
I see a few different things wrong. Consider:
where (age<&&t&I. and (%Do I = &I. %to 5;
MilkF&&t&I. ne 0 or MilkA&&t&I. ne 0 ;
%end;) ) ;
This would generate:
where (age<30 and (
MilkF30 ne 0 or MilkA30 ne 0 ;
MilkF40 ne 0 or MilkA40 ne 0 ;
MilkF50 ne 0 or MilkA50 ne 0 ;
MilkF60 ne 0 or MilkA60 ne 0 ;
MilkF70 ne 0 or MilkA70 ne 0 ;
) ) ;
If you look at the generated WHERE statement, it's invalid due to the extra semicolons. This causes the error message. To fix it, remove the semicolon in your macro at the end of MilkA&&t&I. ne 0 ;
The logic of those OR clauses doesn't look to me like what you describe in the comment, so you might want to check.
As #Reeza points out, you are using the counter &i inside a loop that is using a counter &i. This typically won't cause a syntax error, but will cause either the outer loop to exit prematurely, or continue infinite looping. So you could use &j as the iterator for the inner loop. Also a good idea to declare &i and &j as %LOCAL to the macro, to avoid collisions with any similarly named macro variables in outer scopes.
UPDATE:
Suggest you turn on OPTIONS MPRINT, and look at the code generated by the macro. With your edit:
where (age<&&t&I. and
(%do I = &I. %to 5;
MilkFreq&&t&I. ne 0 or MilkAmnt&&t&I. ne 0 or
%end;
) ) ;
you now have one too many ORs, because it will generate an extra OR at the end, i.e. MilkFreq70 ne 0 or MilkAmnt70 ne 0 or ) )
You could try something like:
where (age < &&t&i and
(%do j = &i %to 5;
MilkFreq&&t&j ne 0 or MilkAmnt&&t&j ne 0
%if &j ne 5 %then or;
%end;
) ) ;

Want to extract the first letter of each word

I basically have a variable COUNTRY along with variables SUBJID and TREAT and I want to concatenate it like this ABC002-123 /NZ/ABC.
Suppose if the COUNTRY variable had the value 'New Zealand'. I want to extract the first letter of each word, But I want extract only the first two letters of the value when there is only one word in the COUNTRY variable. I wanted a to know how to simply the below code. If possible in perl programming.
If COUNTW(COUNTRY) GT 1 THEN
CAT_VAR=
UPCASE(SUBJID||"/"||CAT(SUBSTR(SCAN(COUNTRY,1,' '),1,1),
SUBSTR(SCAN(COUNTRY,2,' '),1,1))||"/"||TREAT);
my #COUNTRY = ("New Zealand", "Germany");
# 'NZ', 'GE'
my #two_letters = map {
my #r = /\s/ ? /\b(\w)/g : /(..)/;
uc(join "", #r);
} #COUNTRY;
The SAS Perl Regular Expression solution is to use CALL PRXNEXT along with PRXPOXN or CALL PRXPOSN (or a similar function, if you prefer):
data have;
infile datalines truncover;
input #1 country $20.;
datalines;
New Zealand
Australia
Papua New Guinea
;;;;
run;
data want;
set have;
length country_letter $5.;
prx_1 = prxparse('~(?:\b([a-z])[a-z]*\b)+~io');
length=0;
start=1;
stop = length(country);
position=0;
call prxnext(prx_1,start,stop,country,position,length);
do while (position gt 0);
matchletter = prxposn(prx_1,1,country);
country_letter = cats(country_letter,matchletter);
call prxnext(prx_1,start,stop,country,position,length);
put i= position= start= stop=;
end;
run;
I realize the OP might not be interested in another answer, but for other users browsing this thread and not wanting to use Perl expressions I suggest the following simple solution (for the original COUNTRY variable):
FIRST_LETTERS = compress(propcase(COUNTRY),'','l');
The propcase functions capitalizes the first letters of each word and puts the other ones in lower case. The compress function with 'l' modifier deletes all lower case letters.
COUNTRY may have any number of words.
How about this:
#!/usr/bin/perl
use warnings;
use strict;
my #country = ('New Zealand', 'Germany', 'Tanzania', 'Mozambique', 'Irish Repuublic');
my ($one_word_letters, $two_word_letters, #initials);
foreach (#country){
if ($_ =~ /\s+/){ # Captures CAPs if 'country' contains a space
my ($first_letter, $second_letter) = ($_ =~ /([A-Z])/g);
my ($two_word_letters) = ($first_letter.$second_letter);
push #initials, $two_word_letters; # Add to array for later
}
else { ($one_word_letters) = ($_ =~ /([A-Z][a-z])/); # If 'country' is only one word long, then capture first two letters (CAP+noncap)
push #initials, $one_word_letters; # Add this to the same array
}
}
foreach (#initials){ # Print contents of the capture array:
print "$_\n";
}
Outputs:
NZ
Ge
Ta
Mo
IR
This should do the job provided there really are no 3 word countries. Easily fixed if there are though...
This should do.
#!/usr/bin/perl
$init = &getInitials($ARGV[0]);
if($init)
{
print $init . "\n";
exit 0;
}
else
{
print "invalid name\n";
exit 1;
}
1;
sub getInitials {
$name = shift;
$name =~ m/(^(\S)\S*?\s+(\S)\S*?$)|(^(\S\S)\S*?$)/ig;
if( defined($1) and $1 ne '' ) {
return uc($2.$3);
} elsif( defined($4) and $4 ne '' ) {
return uc($5);
} else {
return 0;
}
}

Renaming names in a file using another file without using loops

I have two files:
(one.txt) looks Like this:
>ENST001
(((....)))
(((...)))
>ENST002
(((((((.......))))))
((((...)))
I have like 10000 more ENST
(two.txt) looks like this:
>ENST001 110
>ENST002 59
and so on for the rest of all ENSTs
I basically would like to replace the ENSTs in the (one.txt) by the combination of the two fields in the (two.txt) so the results will look like this:
>ENST001_110
(((....)))
(((...)))
>ENST002_59
(((((((.......))))))
((((...)))
I wrote a matlab script to do so but since it loops for all lines in (two.txt) it take like 6 hours to finish, so I think using awk, sed, grep, or even perl we can get the result in few minutes. This is what I did in matlab:
frf = fopen('one.txt', 'r');
frp = fopen('two.txt', 'r');
fw = fopen('result.txt', 'w');
while feof(frf) == 0
line = fgetl(frf);
first_char = line(1);
if strcmp(first_char, '>') == 1 % if the line in one.txt start by > it is the ID
id_fold = strrep(line, '>', ''); % Reomve the > symbol
frewind(frp) % Rewind two.txt file after each loop
while feof(frp) == 0
raw = fgetl(frp);
scan = textscan(raw, '%s%s');
id_pos = scan{1}{1};
pos = scan{2}{1};
if strcmp(id_fold, id_pos) == 1 % if both ids are the same
id_new = ['>', id_fold, '_', pos];
fprintf(fw, '%s\n', id_new);
end
end
else
fprintf(fw, '%s\n', line); % if the line doesn't start by > print it to results
end
end
One way using awk. FNR == NR process first file in arguments and saves each number. Second condition process second file, and when first field matches with a key in the array modifies that line appending the number.
awk '
FNR == NR {
data[ $1 ] = $2;
next
}
FNR < NR && data[ $1 ] {
$0 = $1 "_" data[ $1 ]
}
{ print }
' two.txt one.txt
Output:
>ENST001_110
(((....)))
(((...)))
>ENST002_59
(((((((.......))))))
((((...)))
With sed you can at first run only on two.txt you can make a sed commands to replace as you want and run it at one.txt:
First way
sed "$(sed -n '/>ENST/{s=.*\(ENST[0-9]\+\)\s\+\([0-9]\+\).*=s/\1/\1_\2/;=;p}' two.txt)" one.txt
Second way
If files are huge you'll get too many arguments error with previous way. Therefore there is another way to fix this error. You need execute all three commands one by one:
sed -n '1i#!/bin/sed -f
/>ENST/{s=.*\(ENST[0-9]\+\)\s\+\([0-9]\+\).*=s/\1/\1_\2/;=;p}' two.txt > script.sed
chmod +x script.sed
./script.sed one.txt
The first command will form the sed script that will be able to modify one.txt as you want. chmod will make this new script executable. And the last command will execute command. So each file is read only once. There is no any loops.
Note that first command consist from two lines, but still is one command. If you'll delete newline character it will break the script. It is because of i command in sed. You can look for details in ``sed man page.
This Perl solution sends the modified one.txt file to STDOUT.
use strict;
use warnings;
open my $f2, '<', 'two.txt' or die $!;
my %ids;
while (<$f2>) {
$ids{$1} = "$1_$2" if /^>(\S+)\s+(\d+)/;
}
open my $f1, '<', 'one.txt' or die $!;
while (<$f1>) {
s/^>(\S+)\s*$/>$ids{$1}/;
print;
}
Turn the problem on its head. In perl I would do something like this:
#!/usr/bin/perl
open(FH1, "one.txt");
open(FH2, "two.txt");
open(RESULT, ">result.txt");
my %data;
while (my $line = <FH2>)
{
chomp(line);
# Delete leading angle bracket
$line =~ s/>//d;
# split enst and pos
my ($enst, $post) = split(/\s+/, line);
# Store POS with ENST as key
$data{$enst} = $pos;
}
close(FH2);
while (my $line = <FH1>)
{
# Check line for ENST
if ($line =~ m/^>(ENST\d+)/)
{
my $enst = $1;
# Get pos for ENST
my $pos = $data{$enst};
# make new line
$line = '>' . $enst . '_' . $pos . '\n';
}
print RESULT $line;
}
close(FH1);
close(RESULT);
This might work for you (GNU sed):
sed -n '/^$/!s|^\(\S*\)\s*\(\S*\).*|s/^\1.*/\1_\2/|p' two.txt | sed -f - one.txt
Try this MATLAB solution (no loops):
%# read files as cell array of lines
fid = fopen('one.txt','rt');
C = textscan(fid, '%s', 'Delimiter','\n');
C1 = C{1};
fclose(fid);
fid = fopen('two.txt','rt');
C = textscan(fid, '%s', 'Delimiter','\n');
C2 = C{1};
fclose(fid);
%# use regexp to extract ENST numbers from both files
num = regexp(C1, '>ENST(\d+)', 'tokens', 'once');
idx1 = find(~cellfun(#isempty, num)); %# location of >ENST line
val1 = str2double([num{:}]); %# ENST numbers
num = regexp(C2, '>ENST(\d+)', 'tokens', 'once');
idx2 = find(~cellfun(#isempty, num));
val2 = str2double([num{:}]);
%# construct new header lines from file2
C2(idx2) = regexprep(C2(idx2), ' +','_');
%# replace headers lines in file1 with the new headers
[tf,loc] = ismember(val2,val1);
C1( idx1(loc(tf)) ) = C2( idx2(tf) );
%# write result
fid = fopen('three.txt','wt');
fprintf(fid, '%s\n',C1{:});
fclose(fid);

string.find using directory path in Lua

I need to translate this piece of code from Perl to Lua
open(FILE, '/proc/meminfo');
while(<FILE>)
{
if (m/MemTotal/)
{
$mem = $_;
$mem =~ s/.*:(.*)/$1/;
}
elseif (m/MemFree/)
{
$memfree = $_;
$memfree =~ s/.*:(.*)/$1/;
}
}
close(FILE);
So far I've written this
while assert(io.open("/proc/meminfo", "r")) do
Currentline = string.find(/proc/meminfo, "m/MemTotal")
if Currentline = m/MemTotal then
Mem = Currentline
Mem = string.gsub(Mem, ".*", "(.*)", 1)
elseif m/MemFree then
Memfree = Currentline
Memfree = string.gsub(Memfree, ".*", "(.*)", 1)
end
end
io.close("/proc/meminfo")
Now, when I try to compile, I get the following error about the second line of my code
luac: Perl to Lua:122: unexpected symbol near '/'
obviously the syntax of using a directory path in string.find is not like how I've written it. 'But how is it?' is my question.
You don't have to stick to Perl's control flow. Lua has a very nice "gmatch" function which allows you to iterate over all possible matches in a string. Here's a function which parses /proc/meminfo and returns it as a table:
function get_meminfo(fn)
local r={}
local f=assert(io.open(fn,"r"))
-- read the whole file into s
local s=f:read("*a")
-- now enumerate all occurances of "SomeName: SomeValue"
-- and assign the text of SomeName and SomeValue to k and v
for k,v in string.gmatch(s,"(%w+): *(%d+)") do
-- Save into table:
r[k]=v
end
f:close()
return r
end
-- use it
m=get_meminfo("/proc/meminfo")
print(m.MemTotal, m.MemFree)
To iterate a file line by line you can use io.lines.
for line in io.lines("/proc/meminfo") do
if line:find("MemTotal") then --// Syntactic sugar for string.find(line, "MemTotal")
--// If logic here...
elseif --// I don't quite understand this part in your code.
end
end
No need to close the file afterwards.