SAS Input Statement - encryption

I have an autoexec file that encrypts my password when I'm connecting to different servers....the code looks as follows:
%global wspwd ewspwd hpwd ehpwd ;
/* Enter WORKSTATION Password Below */
filename ewspwdfl "/home/&sysuserid./ewspwd.txt" ;
proc pwencode in=’XXXXXXXX’ out=ewspwdfl ; run ;
data _null_ ;
infile ewspwdfl obs=1 length=l ;
input # ;
input #1 line1 $varying1024. l ;
call symput('ewspwd',cats(substr(line1,1,l))) ;
call symput('wspwd',cats(‘XXXXXXXX’)) ;
run ;
My question is: why is
input # ;
included and why
input #1 line1 $varying1024. l ;
doesn't suffice.
Whenever I have created datasets with SAS I have never had to include "input #;" in my statement. I just simply write something along the lines of:
input #1 firstname $ #15 lastname $ #30 date mmddyy6.;

You don't need it for that data step. You could simplify it to.
data _null_ ;
infile ewspwdfl obs=1 TRUNCOVER ;
input line1 $CHAR1024. ;
call symputX('ewspwd',line1);
call symputX('wspwd',‘XXXXXXXX’) ;
run ;
Using input # is a good way to create a program where you want to read different lines using different input statements. You could test the content of the _infile_ variable and execute different parts of the data step based on what is read.
It is also useful when using the EOV= option on the INFILE statement to detect when you are starting to read from a new file, since it is not set until you begin reading the new file. So the INPUT # gets SAS to begin reading so that the EOV variable is set, but keeps the line waiting for your real INPUT statement to read later.
The #1 is useful if you want to read the same columns over again into different variables. For example you might want to read the first few characters as a string and then test them and based on what you find re-read as a number or a date.

Related

How do I generate proc steps dynamically?

I have a number of repetitive lines of code which I want to be able to generate dynamically, the result should look something like this:
PROC SURVEYSELECT DATA=IBIS3_4 (where = (Exkl_UtgUtl_Flyg=3)) METHOD=SRS SAMPSIZE=&Exkl_UtgUtl_Flyg_kvot SEED=1234567 OUT=Exkl_UtgUtl_Flyg;
RUN;
PROC SURVEYSELECT DATA=IBIS3_4 (where = (Exkl_UtgUtl_Tag=3)) METHOD=SRS SAMPSIZE=&Exkl_UtgUtl_Tag_kvot SEED=1234567 OUT=Exkl_UtgUtl_Tag;
RUN;
I can do generate the SAS-code quite easily in R. I just define a vector with the relevant strings:
strings<-c("Exkl_UtgUtl_Flyg",
"Exkl_UtgUtl_Tag",
"Exkl_UtgUtl_Farja",
"Exkl_UtgUtl_Taxi",
"Exkl_UtgUtl_Hyrbil",
"Exkl_UtgUtl_Driv",
"Exkl_UtgUtl_Bo",
"Exkl_UtgUtl_Resta",
"Exkl_UtgUtl_Shop",
"Exkl_UtgUtl_Aktiv",
"Exkl_UtgUtl_Annat",
"Exkl_UtgSwe_Flyg",
"Exkl_UtgSwe_Tag",
"Exkl_UtgSwe_Farja",
"Exkl_UtgSwe_Taxi",
"Exkl_UtgSwe_Hyrbil",
"Exkl_UtgSwe_Driv",
"Exkl_UtgSwe_Bo",
"Exkl_UtgSwe_Resta",
"Exkl_UtgSwe_Shop",
"Exkl_UtgSwe_Aktiv",
"Exkl_UtgSwe_Annat")
And then I define a for-loop:
for (i in strings){print(paste0("* ",i," *
PROC SURVEYSELECT DATA=IBIS3_4 (where = (",i,"=3)) METHOD=SRS
SAMPSIZE=&",i,"_kvot SEED=1234567
OUT=",i,";
RUN;"))}
I have to copy-paste the output into MS Word and remove all quotes/row numbers/rowbreak-signs, but at least I don't have to program 20+ identical lines of code manually.
But is there a way of doing this entirily in SAS? Can I put a do-loop inside a put-statement or something similar, in order to generate the code that I need?
Try if this works. Without your data I cannot test it properly.
You don't need to create a new table for this. If those strings are part of a different table, just call that column in the last part.
data have;
input strings $40.;
datalines;
Exkl_UtgUtl_Tag
Exkl_UtgUtl_Farja
Exkl_UtgUtl_Taxi
Exkl_UtgUtl_Hyrbil
;
run;
Create a macro that will spam those proc surveyselect statements.
%macro Survey_select(strings);
PROC SURVEYSELECT DATA=IBIS3_4
(where = (&strings=3)) METHOD=SRS SAMPSIZE=%sysfunc(cats(&,&strings,_kvot)) SEED=1234567 OUT=&strings
;
RUN;
%mend Survey_select;
Call your macro with data step using call execute. If those strings are not in a table of their own, you can easily call them from some other table in this step.
data _null_;
set have;
call execute(cats('%Survey_select(',strings,')'));
run;

How can I split a.csv file in Progress4GL if it exceeds 1000000 lines

In my program I am outputting a .csv file which exceeds 1000000 lines. Currently after the file is exported, I am splitting the file from linux using the below commands. However, I would like to know if we can split the files using a progress code. If so, could someone plese let me know on how to do it.
Below is the linux command I use manually.
ls -l xa*
split -1000000 filename.csv
mv xaa filename-01.csv
mv xab filename-02.csv
Without any code to work with I invented some code outputting to different files. You will have to work with OUTPUT TO and set new filenames.
This example will output 1050 lines split in files of 100 lines each.
DEFINE VARIABLE iLinesToOutput AS INTEGER NO-UNDO INIT 1050.
DEFINE VARIABLE iSplitAt AS INTEGER NO-UNDO INIT 100.
DEFINE VARIABLE iLine AS INTEGER NO-UNDO.
DEFINE VARIABLE cFile AS CHARACTER NO-UNDO.
DEFINE VARIABLE iFile AS INTEGER NO-UNDO.
DEFINE VARIABLE iOpen AS INTEGER NO-UNDO.
DEFINE STREAM str.
DO iLine = 1 TO iLinesToOutput:
// Open a new stream/file
IF (iLine - 1 ) MOD iSplitAt = 0 THEN DO:
iFile = iFile + 1.
cFile = "c:\temp\file-" + STRING(iFile, "999") + ".txt".
OUTPUT STREAM str TO VALUE(cFile).
EXPORT STREAM str DELIMITER "," "Customer ID" "Order Number" "Contact" "Count"
END.
// Output some data
PUT STREAM str UNFORMATTED "Line " iLine SKIP.
// Close the stream/file
IF iLine MOD iSplitAt = 0 THEN DO:
OUTPUT STREAM str CLOSE.
END.
END.
/* Close last file if not exactly right number of lines */
/* This could also be checked but close twice doesn't really matter */
OUTPUT STREAM str CLOSE.

ConvertFrom-StringData values stored in variable

I'm new to Powershell and I'm putting together a script that will populate all variables from data stored in a Excel file. Basically to create numerous VMs.
This works fine apart from where i have a variable with multiple name/value pairs which powershell needs to be a hashtable.
As each VM will need multiple tags applying, i have a column in excel called Tags.
The data in the field would look something like: "Top = Red `n Bottom = Blue".
I'm struggling to use ConvertFrom-StringData to create the hashtable however for these tags.
If i run:
ConvertFrom-StringData $exceldata.Tags
I end up with something like:
Name Value
---- -----
Top Red `n bottom = blue
I need help please with formatting the excel field correctly so ConvertFrom-StringData properly creates the hashtable. Or a better way of achieving this.
Thanks.
Sorted it, formatted the excel field as: Dept=it;env=prod;owner=Me
Then ran the following commands. No ConvertFrom-StringData required.
$artifacts = Import-Excel -Path "C:\temp\Artifacts.xlsx" -WorkSheetname "VM"
foreach ($artifact in $artifacts) {
$inputkeyvalues = $artifact.Tags
# Create hashtable
$tags = #{}
# Split input string into pairs
$inputkeyvalues.Split(';') |ForEach-Object {
# Split each pair into key and value
$key,$value = $_.Split('=')
# Populate $tags
$tags[$key] = $value
}
}

unix shell scripting to find and remove unwanted string in a pipe delimited file in a particular column

{
I have a requirement, where the file is pipe "|" delimited.
The first row contains the headers, and the count of columns is 5.
I have to delete only the string in the 3rd column if it matches the pattern.
Also note the 3rd column can contain strings with commas ,, semicolon ; or colon : but it will never contain a pipe | (due to which we have chosen a pipe delimiter).
Input File:
COL1|COL2|COL3|COL4|COL5
1|CRIC|IPL|CRIC1:IPL_M1;IPL_M2;TEST_M1,CRIC2:ODI_M1;IPL_M3|C1|D1
2|CRIC|TEST|CRIC1:TEST_M2,CRIC2:ODI_M1;IPL_M1;TEST_M2;IPL_M3;T20_M1|C2|D2
Output should change only in COL3 no other columns should be changed, i.e. in COL3 the string which matches the pattern 'IPL_' should be present.
Any other strings like "TEST_M1","ODI_M1" should be made null.
And any unwanted semi colons should be removed.
eg
Question - CRIC1:IPL_M1;IPL_M2;TEST_M1,CRIC2:ODI_M1;IPL_M3
result - CRIC1:IPL_M1;IPL_M2,CRIC2:IPL_M3
Another scenario where if only strings that do not match "IPL_" are present then
Question - CRIC1:TEST_M1,CRIC2:ODI_M1
Result - CRIC1:,CRIC2:
Output File:
COL1|COL2|COL3|COL4|COL5
1|CRIC|IPL|CRIC1:IPL_M1;IPL_M2,CRIC2:IPL_M3|C1|D1
2|CRIC|TEST|CRIC1:,CRIC2:IPL_M1;IPL_M3|C2|D2
Basic requirement is to find and replace the string,
INPUT
COL1|COL2|COL3|COL4|COL5
1|A1|A12|A13|A14|A15
Replace A13 with B13 in column 3 (A13 can change, I mean we have to find any pattern like A13)
OUTPUT
COL1|COL2|COL3|COL4|COL5
1|A1|A12|B13|A14|A15
Thanks in advance.
Re formatting the scenario in simpler terms,by taking only 2 columns, where I need to search "IPL_" and keep only those strings and any other string like "ODI_M3;TEST_M5" should be deleted
{
I/P:
{
COL1|COL2
CRIC1|IPL_M1;IPL_M2;TEST_M1
CRIC2|ODI_M1;IPL_M3
CRIC3|ODI_M3;TEST_M5
CRIC4|IPL_M5;ODI_M5;IPL_M6
}
O/P:
{
COL1|COL2
CRIC1|IPL_M1;IPL_M2
CRIC2|IPL_M3
CRIC3|
CRIC4|IPL_M5;IPL_M6
}
Awaiting your precious suggestions.
Please help I'm new to this platform.
Thanks,
Saquib
}
If I'm reading this correctly (and I'm not entirely sure I am; I'm going mostly by the provided examples), then this could be done relatively sanely with Perl:
#!/usr/bin/perl
while(<>) {
if($. > 1) {
local #F = split /\|/;
$F[3] = join(",", map {
local #H = split /:/;
$H[1] = join(";", grep(/IPL_/, split(";", $H[1])));
join ":", #H;
} split(/,/, $F[3]));
$_ = join "|", #F;
}
print;
}
Put this code into a file, say foo.pl, then if your data is in a file data.txt you can run
perl -f foo.pl data.txt
This works as follows:
#!/usr/bin/perl
# Read lines from input (in our case: data.txt)
while(<>) {
# In all except the first line (the header line):
if($. > 1) {
# Apply the transformation. To do this, first split the line into fields
local #F = split /\|/;
# Then edit the third field. This has to be read right-to-left at the top
# level, which is to say: first the field is split along commas, then the
# tokens are mapped according to the code in the inner block, then they
# are joined with commas between them again.
$F[3] = join(",", map {
# the map block does a similar thing. The inner tokens (e.g.,
# "CRIC1:IPL_M1;IPL_M2") are split at the colon into the CRIC# part
# (which is to be unchanged) and the value list we want to edit.
local #H = split /:/;
# This value list is again split along semicolons, filtered so that
# only those elements that match /IPL_/ remain, and then joined with
# semicolons again.
$H[1] = join(";", grep(/IPL_/, split(";", $H[1])));
# The map result is the CRIC# part joined to the edited list with a colon.
join ":", #H;
} split(/,/, $F[3]));
# When all is done, rejoin the outermost fields with pipe characters
$_ = join "|", #F;
}
# and print the result.
print;
}

Function to create the array by reading the file

I am creating scripts which will store the contents of pipe delimited file. Each column is stored in a separate array. I then read the information from the arrays and process it. There are 20 pipe delimited files and I need to write 20 scripts. The processing that will happen in each script after the information is stored in the array is different. The number of columns in each pipe delimited file is different (but in no case it would be more than 9 columns). I need to do this activity of storing the information in the array in the beginning of each script. The way I am doing it at present is given below. I want help from you to understand how can I write a function to do this activity.
cat > example_file.txt <<End-of-message
some text first row|other text first row|some other text first row
some text nth row|other text nth row|some other text nth row
End-of-message
# Note that example_file.txt will available. I have created it inside the script just to let you know the format of the file
OIFS=$IFS
IFS='|'
i=0
while read -r first second third ignore
do
first_arr[$i]=$first
second_arr[$i]=$second
third_arr[$i]=$third
(( i=i+1 ))
done < example_file.txt
IFS=$OIFS
Here is a sort-of minimal change to your script that should get you further...
...
...
while read -r first second third ignore
do
arr0[$i]=$first
arr1[$i]=$second
arr2[$i]=$third
(( i=i+1 ))
done < example_file.txt
IFS=$OIFS
proc0 () {
for j in "$#"; do
echo proc0 : "$j"
done
}
proc1 () {
echo proc1
}
proc2 () {
echo proc2
}
for i in 0 1 2; do
t=arr$i'[#]'
proc$i "${!t}"
done

Resources