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
Related
I have a problem when I try to merge 2 variables into one.
Example:
$MyA = #{
foo = 1..10
bar = 2,4,6,8,10,12,14,16,18,20
}
$MyB = #{
foo = 1..30
bar = ""
}
Foreach ($_ in $MyA) {Where $MyB.foo -eq $_.foo; $MyB.bar = $_.bar}
What I want to do is: Insert $MyA.bar into $MyB.bar where the property foo matches....
But it ends up with this error message:
Where-Object : A positional parameter cannot be found that accepts argument 'System.Object[]'.
What I want to end up with is something like this:
$MyB
foo bar
--------
1 2
2 4
etc. until foo is grater than 10 then bar should be empty
Please help!
I am trying to build an array of possible IP addresses based on a user's input. i.e. IP address along with a CIDR number. My end goal is to compare this list with a separate list of addresses and find which are is missing.
Example
user input: 192.168.1.0 /24
I want to build an array for all possible values for the /24 network (i.e. the IP address can be anywhere from 192.168.1.0 - 192.168.1.255)
In order for this to work, I think I have to convert the IP address to binary and then find the bits that will be the host part of the network, which I've done here:
function ConvertToBinary{
param($ipAddress)
[string]$binaryIP = -join ($ipAddress.Split('.') | ForEach-Object {[System.Convert]::ToString($_,2).PadLeft(8,'0')})
return $binaryIP
}
function FindHost{
param(
[string]$binaryIPAddress,
[int32]$CIDR
)
$hostBits = 32-$CIDR
[string]$myHost = $binaryIPAddress.Substring($binaryIPAddress.Length-$hostBits)
return $myHost
}
$myip = ConvertToBinary "192.168.3.1"
$myHost = FindHost $myip 8
I'm a little stuck on how to proceed, so if anyone can help me out or point me in the right direction, it would be much appreciated
I wrote this using some similar questions to get the first and last address for any subnet given a random IP and mask:
Function Get-SubnetAddresses {
Param ([IPAddress]$IP,[ValidateRange(0, 32)][int]$maskbits)
# Convert the mask to type [IPAddress]:
$mask = ([Math]::Pow(2, $MaskBits) - 1) * [Math]::Pow(2, (32 - $MaskBits))
$maskbytes = [BitConverter]::GetBytes([UInt32] $mask)
$DottedMask = [IPAddress]((3..0 | ForEach-Object { [String] $maskbytes[$_] }) -join '.')
# bitwise AND them together, and you've got the subnet ID
$lower = [IPAddress] ( $ip.Address -band $DottedMask.Address )
# We can do a similar operation for the broadcast address
# subnet mask bytes need to be inverted and reversed before adding
$LowerBytes = [BitConverter]::GetBytes([UInt32] $lower.Address)
[IPAddress]$upper = (0..3 | %{$LowerBytes[$_] + ($maskbytes[(3-$_)] -bxor 255)}) -join '.'
# Make an object for use elsewhere
Return [pscustomobject][ordered]#{
Lower=$lower
Upper=$upper
}
}
Usage looks like:
Get-IPAddresses 10.43.120.8 22
Lower Upper
----- -----
10.43.120.0 10.43.123.255
And I put this together to generate the whole list. I'm sure this could be done better, but the simple instructions run fast enough:
Function Get-IPRange {
param (
[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName)][IPAddress]$lower,
[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName)][IPAddress]$upper
)
# use lists for speed
$IPList = [Collections.ArrayList]::new()
$null = $IPList.Add($lower)
$i = $lower
# increment ip until reaching $upper in range
while ( $i -ne $upper ) {
# IP octet values are built back-to-front, so reverse the octet order
$iBytes = [BitConverter]::GetBytes([UInt32] $i.Address)
[Array]::Reverse($iBytes)
# Then we can +1 the int value and reverse again
$nextBytes = [BitConverter]::GetBytes([UInt32]([bitconverter]::ToUInt32($iBytes,0) +1))
[Array]::Reverse($nextBytes)
# Convert to IP and add to list
$i = [IPAddress]$nextBytes
$null = $IPList.Add($i)
}
return $IPList
}
Converting to [IPAddress] on the last line is nice for validating all the results are real, but probably not necessary if you just want the ipv4 strings. Usage:
Get-SubnetAddresses 10.43.120.8 30 | Get-IPRange | Select -ExpandProperty IPAddressToString
10.43.120.8
10.43.120.9
10.43.120.10
10.43.120.11
Sources:
https://stackoverflow.com/a/51307519/7411885
https://stackoverflow.com/a/58821442/7411885
The script should allocate and list the numbers of the particular foods.
So i want a Script that makes a "Plan" of the Food i'm gonna eat this week.
i have a counter that can "choose from 200 meals".
So what i need is that it is like this:
number* = Tacos
number* = Pizza
...continuing like this
I don't have a clue how to do this... i hope you can help me!
here is my code untill now.
# Food selector for the week!
#random Stuff mixed for every day.
function Random-Food{
Param(
[array]$InputList
)
return $InputList | Get-Random -Count 7
$InputList.Count;
}
$a = 1..200
Write-Output (Random-Food -InputList $a)
In a separate file, create an enumeration (for readability):
Enum Food
{
Tacos
Pizza
...
}
In your script, dot-source this file so you have it available: . 'C:\myenum.ps1'
Then in your function:
function Get-Food {
[Enum]::GetValues([Food]) | Get-Random -Count 7
}
This will return 7 random foods from your list. As an added bonus, you can assign a number to each food and get that reference as well. (Tacos = 5 and access it like [Food]::Tacos which can be treated as a string or integer)
To include the day:
function Get-Food {
$foodsOfWeek = [Enum]::GetValues([Food]) | Get-Random -Count 7
foreach ($day in [Enum]::GetValues([DayOfWeek])) {
([string]$day).Substring(0, 3) + ': ' + $foodsOfWeek[$day]
}
}
An explanation:
The function will grab all the values (Tacos, Pizza, etc.) from your custom [Food] enumeration and then randomly select 7 of those to put into an array that we store into $foodsOfWeek.
At that point, we need to loop through the days of the week (system built-in enumeration [DayOfWeek]). We type-cast to a string so we can call the .Substring() method to grab the first 3 characters and append a : for formatting.
Finally, we access the $foodOfWeek array using the [DayOfWeek] enumeration since days correlate to the numbers 0-6 (the size of our array).
(DayOfWeek.Sunday == 0) && (DayOfWeek.Sunday == "Sunday")) == true
To learn PowerShell, try this: Windows PowerShell 3.0 Step by Step - Pearsoncmg.com
Freebie follows. Written and tested on A Win 7 machine with PowerShell 4. I included a few foods. All you have to do is add items to the $foodList.
Sample output follows the code
cls
#define an array of strings and load it with food words
$foodList = #("Apples","Bananas", "Cherries","Grapes","Mangoes","Melons","Oranges","Peaches","Pears","Pineapples","Strawberries","Tomatoes")
#define an array of days
$dow = #("Mon","Tue","Wed","Thu","Fri","Sat","Sun")
#loop 7 times, once per day and generate a random number between 0 and the number of food items - 1
for ($i = 0; $i -lt 7; $i++)
{
$foodItemIndex = Get-Random -Minimum 0 -Maximum ($foodList.Count - 1)
write-host ($dow[$i] + ": " + $foodList[$foodItemIndex])
}
Here's one way to do it:
$foodChoices = "Pizza","Taco","Dim Sum","Burger","Salad","Soup","Sandwich","Curry"
$foodChoices[(Get-Random -InputObject (0..($foodChoices.Count - 1)) -Count 7)] |
ForEach-Object {$daysOfWeek = [enum]::GetNames([DayOfWeek]); $day=0}{
[PsCustomObject]#{
Day=$daysOfWeek[$day++]
Food=$_
}
}
Output will look something like this:
Day Food
--- ----
Sunday Burger
Monday Sandwich
Tuesday Salad
Wednesday Taco
Thursday Soup
Friday Dim Sum
Saturday Curry
Just add more items to $foodChoices and you're good to go ...
I am trying to mask fields in a string as seen below. It is working to an extent, half way really. At some stage after the $addresspostcode the replacement characters aren't replacing for the correct positions. Would anyone have an idea of a fix?
The adressee0 line is from the output file
ADDRESSEE0|XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|YYYYYYYYYYYYYYYYYYYYYYYYYYYYYY|YYYYYYYYYYYYYYYYYYYYYYYYYYYYYY|YYYYYYYYYYYYYYYYYYYYYYYYYYYYYY|YYYYYYYYYYYYYYYYYYYYYYYYYYYYYY|ZZZZZZZZ|Sir or MadamZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ |A1|OM|Mr Patrick MurphyZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|45 CregtownZZZZZZZZZZZZZZZZ |EastRoad RoadZZZZZZZZZZZZZZZZ |TownnamersZZZZZZZZZZZZZZZZ |CityAB 16ZZZZZZZZZZZZZZZZ |ZZZZZZZZ| |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|ZZZZZZZZZZZZZZZZ |Sir or MadamZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ |IA|3319041| | |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXZZZZZZZZ
ForEach-Object {
$addresseeName = $_.Substring(11,50)
$addresseeName2 = $_.Substring(62,50)
$addresseeLine1 = $_.Substring(113,30)
$addresseeLine2 = $_.Substring(144,30)
$addresseeLine3 = $_.Substring(175,30)
$addresseeLine4 = $_.Substring(206,30)
$addresseePostCode = $_.Substring(237,8)
$referenceAddressName1 = $_.Substring(303,50)
$referenceAddressName2 = $_.Substring(354,50)
$referenceAddresseeLine1 = $_.Substring(405,30)
$referenceAddresseeLine2 = $_.Substring(436,30)
$referenceAddresseeLine3 = $_.Substring(467,30)
$referenceAddresseeLine4 = $_.Substring(498,30)
$mask50 = 'X' * 50
$mask30 = 'Y' * 30
$mask08 = 'Z' * 8
# IF statement, if the string is at position 0-10, and begins with 'ADDRESSEE0'
# then run replace statement
if ($_.Substring(0,10) -eq 'ADDRESSEE0') {
$_.Replace($addresseeName, $mask50).Replace($addresseeName2, $mask50).Replace($addresseeLine1, $mask30).Replace($addresseeLine2, $mask30).Replace($addresseeLine3, $mask30).Replace($addresseeLine4, $mask30).Replace($addresseePostCode, $mask08).Replace($referenceAddressName1, $mask50).Replace($referenceAddressName2, $mask50).Replace($referenceAddresseeLine1, $mask30).Replace($referenceAddresseeLine2, $mask30).Replace($referenceAddresseeLine3, $mask30).Replace($referenceAddresseeLine4, $mask30)
The problem with your code is that it does the replace based on the contents of the string (rather than the position). This means if the same text exists elsewhere in the string, it replaces that text also, breaking the later replaces.
I suggest you do this instead:
$mask50 = 'X' * 50
$mask30 = 'Y' * 30
$mask08 = 'Z' * 8
$SomeInput | ForEach-Object {
if ($_.Substring(0,10) -eq 'ADDRESSEE0')
{
$SplitString = $_.Split('|')
1..2 | ForEach-Object { $SplitString[$_] = $mask50 }
3..6 | ForEach-Object { $SplitString[$_] = $mask30 }
$SplitString[7] = $mask08
8..9 | ForEach-Object { $SplitString[$_] = $mask50 }
10..13 | ForEach-Object { $SplitString[$_] = $mask30 }
$SplitString -Join '|'
}
}
This splits the string based on the | character and then does individual replaces for each (we use the .. array notation to make this a little more efficient).
Then we join the string again at the end with the | character.
Assuming ADDRESSEE0|... is the input string, why not split the data first? This provides manageable chunks instead of one giant string with truckload of method chaining. Like so,
# Get input data
$raw = `'ADDRESSEE0|ADDRESSEE1|ADDRESSEELine0|ADDRESSEELine1|...'`
# Split the string by each pipe | char. This uses regex syntax, so escape \ is needed
$lines = $raw -split '\|'
# Assign splitted elements into more readable variables
$addresseeName = $lines[0]
$addresseeName2 = $lines[1]
$addresseeLine1 = $lines[2]
...
# mask the data whatever way floats your boat
$addresseeName = $addresseeName.substring(0,9) + $mask08
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