Is it possible to use with and endwith instructions? - powershell

I am translating vba code to PowerShell
Does with and endwith exist in PowerShell?
What is the alternative?
Could you give me an example?

The VBA With ... End With statement is just a shorthand syntax - see With...End With Statement (Visual Basic):
With objectExpression
[ statements ]
End With
So, for example this VBA script:
With MyVar
.PropertyX = 100
.PropertyY = "myvalue"
.MyMethod()
End With
is equivalent to this VBA script:
MyVar.Property = 100
MyVar.PropertyY = "myvalue"
Myvar.MyMethod()
which translates simply to this in PowerShell:
$myVar.PropertyX = 100
$myVar.PropertyY = "myvalue"
$myvar.MyMethod()
However, if the objectExpression is a longer expression you can just assign it to a temporary variable:
With MyVar.MyProperty.MyOtherProperty
.PropertyX = 100
.PropertyY = "myvalue"
.MyMethod()
End With
becomes this instead in VBA:
MyTempVar = MyVar.MyProperty.MyOtherProperty
MyTempVar.PropertyX = 100
MyTempVar.PropertyY = "myvalue"
MyTempVar.MyMethod()
which translates to PowerShell as follows:
$myTempVar = $myVar.MyProperty.MyOtherProperty
$myTempVar.PropertyX = 100
$myTempVar.PropertyY = "myvalue"
$myTempVar.MyMethod()

Best alternative in Powershell:
foreach ($_ in $MyVar) {
$_.PropertyX = 100
$_.PropertyY = "myvalue"
$_.MyMethod()
}
At least I like it...

Related

Declare a here-string containing variables outside of a loop

I would like to declare a here-string outside of a loop use that string in the loop where the variables get resolved.
My ideal scenario would look like below. This doesn't work as Powershell evaluates the string one time before entering the loop instead of each time inside the loop kind of obvious but bitten by it nevertheless.
$number = "Number $($_)"
1..2 | % { $number }
I know I can use one of these solutions
1..2 | % { "Number $($_)" }
$number = "Number {0}"
1..2 | % { $number -f $_ }
$number = "Number <replace>"
1..2 | % { $number -replace "<replace>", "$_" }
but they have drawbacks I'd like to avoid
Due to the size of the string, declaring it inside the loop obfuscates the logic of the loop making the code less readable.
The formatting solution is too easy to get wrong when many variables are involved.
In the replace solution it's easier to match what get's replaced by what variable but I would have to chain many replace commands.
Edit
Rereading my own question makes it obvious that the actual use case is missing from the question.
Note that ultimately I ended up choosing the formatting option
Following would declare the template with some variables that need replacing in a loop
$sqltemplate = #"
SELECT aud.dpt_mov_hex||aud.dpt_ref||aud.can_typ||TO_CHAR(aud.dte_aud-1,'YYYYMMDD')||'000001' transaction_id,
acc.dos_nbr contract_id, acc.pay_acc_nbr account_id,
CASE WHEN NULL IS NULL THEN unt.nam_unt ELSE unt.nam_unt||'<'||NULL ||'>' END product_id,
aud.dpt_ref, aud.dpt_mov_hex, aud.dpt_mov_dte uitwerkingsdatum,
CASE WHEN can_typ = 0 THEN 'VZ'||aud.dpt_mov_ven_typ ELSE 'VZ'||aud.dpt_mov_ven_typ||'-CR' END transactietype,
aud.dpt_mov_amt_eur bedrag_in_eur, aud.dte_cnv, aud.dpt_mov_fix_eur, aud.dpt_mov_con_inc, aud.dpt_mov_amt_sgn bedrag_teken,
aud.dpt_mov_amt_unt bedrag_in_units, aud.dpt_mov_amt_rte, aud.dpt_mov_amt_val_pre, aud.dpt_mov_amt_val_aft,
aud.dpt_mov_amt_ioc, aud.dte_exe verwerkingsdatum, aud.exe_mng, aud.cmt, aud.trn_nbr, aud.dte_aud datum_aanlevering, aud.can_typ
FROM lfe_dpt_mov_aud aud, vnv_isr_pay_acc acc, vnv_bel_unt unt
WHERE aud.dte_aud >= TO_DATE('$((Get-Date).ToString('dd.MM.yyyy'))', 'DD.MM.YYYY')
AND aud.dpt_ref = '{0}'
AND acc.pay_acc_nbr = '{1}'
AND unt.inv_unt = '{2}'
UNION
SELECT aud.dpt_mov_hex||aud.dpt_ref||aud.can_typ||TO_CHAR(aud.dte_aud-1,'YYYYMMDD')||'000001' transaction_id,
acc.dos_nbr contract_id, acc.pay_acc_nbr account_id,
CASE WHEN itr_rte IS NULL THEN unt.nam_unt ELSE unt.nam_unt||'<'||itr_rte ||'>' END product_id,
aud.dpt_ref, aud.dpt_mov_hex, aud.dpt_mov_dte uitwerkingsdatum,
CASE WHEN can_typ = 0 THEN 'VZ'||aud.dpt_mov_ven_typ ELSE 'VZ'||aud.dpt_mov_ven_typ||'-CR' END transactietype,
aud.dpt_mov_amt_eur bedrag_in_eur, aud.dte_cnv, aud.dpt_mov_fix_eur, aud.dpt_mov_con_inc, aud.dpt_mov_amt_sgn bedrag_teken,
aud.dpt_mov_amt_unt bedrag_in_units, aud.dpt_mov_amt_rte, aud.dpt_mov_amt_val_pre, aud.dpt_mov_amt_val_aft,
aud.dpt_mov_amt_ioc, aud.dte_exe verwerkingsdatum, aud.exe_mng, aud.cmt, aud.trn_nbr, aud.dte_aud datum_aanlevering, aud.can_typ
FROM lfe_dpt_mov_aud aud, vnv_dpt dpt, vnv_isr_pay_acc acc, vnv_bel_unt unt
WHERE aud.dpt_ref = dpt.dpt_ref
AND dpt.pay_acc = acc.pay_acc_nbr
AND dpt.inv_unt = unt.inv_unt
AND aud.dte_aud >= TO_DATE('$((Get-Date).ToString('dd.MM.yyyy'))', 'DD.MM.YYYY')
AND acc.pay_acc_nbr = '{1}'
AND unt.inv_unt = '{2}'
UNION
"#
and this template would get used in a statement such as this
$rolledbackMatchs is an array of custom object containing the three properties: dtp_ref, pay_acc_nbr and inv_unt.
$rolledbackMatches | ForEach-Object { $sqltemplate -f $_.dpt_ref, $_.pay_acc_nbr, $_.inv_unt }
Couple of approaches come to mind:
dot source here-string assignment from a separate file:
# loop.variables.ps1
$myVar = #"
Stuff going on with $_ in here
"#
and then in the loop itself:
1..2 | % { . .\loop.variables.ps1; <# do stuff with $myVar here #> }
Manually invoke string expansion:
$hereString = #'
Stuff (not yet) going on with $_ in here
'#
1..2 | % { $myVar = $ExecutionContext.InvokeCommand.ExpandString($hereString) }
Wrap it in a scriptblock
(as suggested by PetSerAl)
$stringBlock = {
#"
Stuff going on with $_ in here
"#
}
1..2 | % { $myVar = &$stringBlock}
I'm struggling to understand what you're trying to achieve here.
For a start you never define a here-string you just define $number as a string
A here-string would look like this
$number = #"
Number 4
"#
if all you're trying to do is push a number into a string try this
foreach ($number in (1..3)){
"Number $number"
}
which is close to your desired option and less ambiguous

PowerShell HashTable - self referencing during initialization

I have a theoretical problem - how to reference a hash table during its initialization, for example, to compute a member based other already stated members.
Remove-Variable myHashTable -ErrorAction Ignore
$myHashTable =
#{
One = 1
Two= 2
Three = ??? # following expressions do not work
# $This.One + $This.Two or
# $_.One + $_.Two
# $myHashTable.One + $myHashTable.Two
# ????
}
$myHashTable.Three -eq 3 # make this $true
Any ideas how to do it? Is it actually possible?
Edit:
This was my solution:
$myHashTable =
#{
One = 1
Two= 2
}
$myHashTable.Three = $myHashTable.One + $myHashTable.Two
This won't be possible using the object initializer syntax I'm afraid. While it is possible to use variables, you'll have to compute the values before creating the object.
I cannot recommend this, but you can iterate the initializer twice or more:
(0..1) | %{
$a = #{
One = 1
Two = $a.One + 1
}
}
(0..2) | %{
$b = #{
One = 1
Two = $b.One + 1
Three = $b.Two + 1
}
}
Make sure all calculations are idempotent, i.e. do not depend on a number of iterations.
You can also recur to this...
sometimes when the hashtable is very long
and can be defined only in 2 or three recurrences...
works fine:
$AAA = #{
DAT = "C:\MyFolderOfDats"
EXE = "C:\MyFolderOfExes"
}
$AAA += #{
Data = $AAA.DAT + "\#Links"
Scripts = $AAA.EXE + "\#Scripts"
ScriptsX = $AAA.EXE + "\#ScriptsX"
}
Note in the second part we are just adding ( += ) more items to the first part... but now... we can refer the items in first part
of the hashtable

Variable not defined

I wanted to run this line but unfortunately it throws an error. Any ideas?
Undefined function or variable mr.
Error in Playground (line 23)
X = min(mr);
j = 1;
for i = 1:(resolution1+1)
line(i) = m(a(1))*ab(i)+c;
end
for i = 1:(resolution1)
if or(or(line(i)>ab_y(i) & line(i+1)<ab_y1(i+1),line(i)<ab_y1(i)& line(i+1)>ab_y1(i+1)),line(i)==ab_y1(i))
mr(j) = ab1(i);
rk(j) = ab_y1(j);
j = j+1;
end
end
X = min(mr);
Y = max(mr);
Your condition:
or(or(line(i)>ab_y(i) & line(i+1)<ab_y1(i+1),line(i)<ab_y1(i)& line(i+1)>ab_y1(i+1)),line(i)==ab_y1(i))
returns always false, so the statements inside
mr(j)=ab1(i); rk(j)=ab_y1(j);
are never executed. Therefore variable mr does not exist.
Add an mr = []; statement before the loop to initialize the variable (and also revise your condition why it returns always false).

Sqrt method returns 0

I need a method which returns the standard deviation of the last access time in a folder. Here is the code:
function standardDeviation {
Param(
[Parameter(Mandatory=$true)]
[String]$tPath
)
$moy = moyAge $tPath
$variance = 0
$nbFiles = 1
dir | ForEach-Object {
if (!($_.PsIsContainer)) {
$nbDays = $_.LastAccessTime
$nbDays = $nbDays.Days
$sq = $nbDays - $moy
$sq = $sq*$sq
$variance += $sq
$nbFiles++
}
}
$variance = $variance / $nbFiles
$variance = [math]::sqrt($variance)
}
I don't know why, but it always returns 0. Does someone know why?
(moyAge works, and returns an int (number of days))
Your function doesn't actually return anything, so any call will "return" $null. Either change
$variance = [math]::sqrt($variance)
to
[math]::sqrt($variance)
or add a line
$variance
or
return $variance
at the end of the function.
Also, as #MathiasRJessen pointed out, DateTime objects don't have a Days property. If you want the difference in days between today and a specific date you need to calculate a timespan first:
$nbDays = ((Get-Date).Date - $_.LastAccessTime.Date).Days

Macro that makes a Excel-list/log of mails in an account/folder

Here is my macro. I assumed that I could understand the codes and then edit it and get what I wanted
First: The macro abends whitout any progress at all: "Compile error: userdefined typenot defined"
I dont even know what this meens :)
(I use the macro now in an new and emty workbook)
Sub ListAllItemsInInbox()
Dim OLF As Outlook.MAPIFolder, CurrUser As String
Dim EmailItemCount As Integer, i As Integer, EmailCount As Integer
Application.ScreenUpdating = False
Workbooks.Add ' create a new workbook
' add headings
Cells(1, 1).Formula = "Subject"
Cells(1, 2).Formula = "Recieved"
Cells(1, 3).Formula = "Attachments"
Cells(1, 4).Formula = "Read"
With Range("A1:D1").Font
.Bold = True
.Size = 14
End With
Application.Calculation = xlCalculationManual
Set OLF = GetObject("", _
"Outlook.Application").GetNamespace("MAPI").GetDefaultFolder(olFolderInbox)
EmailItemCount = OLF.Items.Count
i = 0: EmailCount = 0
' read e-mail information
While i < EmailItemCount
i = i + 1
If i Mod 50 = 0 Then Application.StatusBar = "Reading e-mail messages " & _
Format(i / EmailItemCount, "0%") & "..."
With OLF.Items(i)
EmailCount = EmailCount + 1
Cells(EmailCount + 1, 1).Formula = .Subject
Cells(EmailCount + 1, 2).Formula = Format(.ReceivedTime, "dd.mm.yyyy hh:mm")
Cells(EmailCount + 1, 3).Formula = .Attachments.Count
Cells(EmailCount + 1, 4).Formula = Not .UnRead
End With
Wend
Application.Calculation = xlCalculationAutomatic
Set OLF = Nothing
Columns("A:D").AutoFit
Range("A2").Select
ActiveWindow.FreezePanes = True
ActiveWorkbook.Saved = True
Application.StatusBar = False
End Sub
As stated in the MSDN Article, which I have already provided in the comments above, you can bind with MS Outlook either by Late Binding (LB) or Early Binding (EB) from MS Excel.
Now what exactly is LB and EB? For this I would again direct you to another MSDN link.
When you are using EB, you have to set a reference to the relevant application from Tools ~~> References and set your reference to the relevant Outlook Object Library. For example
In Layman Terms, Excel now understands what Outlook and MAPIFolder is. Similarly olFolderInbox is an Outlook constant which has a value of 6. So your original code would have worked without any errors if you would have set the reference.
When you are usign LB, you do not set a reference. You declare your objects as Objects. There are analyzed by Excel during runtime. More about this has already been covered in the link above. Also since we are late binding, we have to explicitly use 6 instead of olFolderInbox as that is what it's value is.
Hope this sets you on the right track. Do ask questions if you have any.