How can I handle multiple options for parameters in powershell? - parsing

I want to be able to have multiple forms of the same parameter like so:
param(
[string]$p or $path = "C:\",
[string]$f or $filter = "*.txt",
[switch]$o or $overwrite
)
but I'm not sure how to do this. Most times, you would only be able to choose one (e.g. only $p or only $path). Is it possible to use multiple names for the same variable/parameter?

Like this:
param(
[Alias('p')]
[string]$path = "C:\",
[Alias('f')]
[string]$filter = "*.txt",
[Alias('o')]
[switch]$overwrite
)
Note you can have multiple aliases too: [Alias('p','thepath')]

PowerShell partial parameter name matching may be what your looking for.
# test.ps1
param($path)
write-host $path
Calling .\test.ps1 with either .\test.ps1 -path "c:\windows" or .\test.ps1 -p "c:\windows" will both match, and populate, the $path parameter.

Related

Tick script with multiple tag values

I have a tick script where I have to run query with where clause on multiple interface_name which looks something like this:
query('''SELECT last("state") as "value" FROM router.autogen.cisco_router where type = 'interface' and host = '10.66.14.82' and ("name"='GigabitEthernet0/0/0' OR "name"='GigabitEthernet0/0/1') ''')
and this query run on influx db but kapacitor change this query to something like this:
`SELECT last("state") as "value" FROM router.autogen.cisco_router where type = 'interface' and host = '10.66.14.82' and (\"name\"='GigabitEthernet0/0/0' OR \"name\"='GigabitEthernet0/0/1')`
how to avoid \ befor " in tick script.
I did not find any answer so i created a new tag with a different name and it solved the problem.

Ignore the "Select Sheet" message on Excel Interop

Maybe someone here can help me out with this. I am trying to convert all XLS to XLSX/M files with powershell and interop. So far so good. In my next step, I have to adapt the link sources in each file, which works sometimes (also from XLS to XLSX/M).
I don’t know why, but sometimes the original worksheet name does not exist in the linked Excel file and results in a pop up with which the user has to interact:
I actually really don’t care so much about the sheet and I just want to ignore the message so that the script can continue.
In my code I use the function ChangeLink, like this:
$workbook.ChangeLink($fileLink_old, $fileLink_new)
I also have deactivated any warning on the excel object itself, but nothing helps:
$excel.DisplayAlerts = $False
$excel.WarnOnFunctionNameConflict = $False
$excel.AskToUpdateLinks = $False
$excel.DisplayAlerts = $False
The most convinient way for me would be just ignoring the pop up.
Is there a way without going through all cells by itself or modifing the externalLinks/_rels inside of the excel file?
Thanks in advance
Stephan
Edit:
To loop through each cell, not really efficient
ForEach ($Worksheet in #($workbook.Sheets)) {
Write-Host $Worksheet.Name
ForEach ($filelink in $fileLinks){
$worksheetname = $null
$fl_we = $fileLink.Substring(0, $fileLink.LastIndexOf('.'))
$found = $Worksheet.Cells.Find($fl_we.Substring(0, $fl_we.LastIndexOf('\')) + '\[' + $fl_we.Substring($fl_we.LastIndexOf('\')+1))
if($found -ne $null){
Write-Host Search $filelink
Write-Host $Worksheet.Cells($found.Row,$found.Column).Formula
$str_formula = $Worksheet.Cells($found.Row,$found.Column).Formula
$worksheetname = $str_formula.Substring($str_formula.IndexOf(']')+1,$str_formula.IndexOf('!')-$str_formula.IndexOf(']')-2)
Write-Host $worksheetname -ForegroundColor DarkGray
#Add worksheets with filename to list
}
}
}
#Check if worksheet exists in linked file

Environment variables manipulation

When using:
echo "${env.PRODUCT_NAME}"
it will echo:
MyProdName
When using:
echo "${env.MyProdName_Key}"
it will echo:
123456789
I would like to use something as follows:
echo "${env.${env.PRODUCT_NAME}_Key}"
Is this possible? How?
In Bash this is termed as variable in direction
Try using variables to make it further simplified
PRODUCT_NAME=$(echo "${env.PRODUCT_NAME}")
This would assign PRODUCT_NAME=MyProdName
Similarly
MyProdName=$(echo "${env.MyProdName_Key}")
This would assign MyProdName=123456789
Now when you print PRODUCT_NAME value you will get
echo ${PRODUCT_NAME}
MyProdName
And adding '!' variable indirection will give you the value of another variable values
echo ${!PRODUCT_NAME}
123456789
Maybe this will help you somehow:
def env = [
PRODUCT_NAME:'MyProdName',
MyProdName_Key: 123456789,
]
println "${env[env.PRODUCT_NAME+'_Key']}"
env is Map in the example provided but it works in the exactly same way.
Important note, regardless of how you're deriving variables:
There's no need to use string interpolation if the only value in a
string is a variable itself. This just clutters your code.
Instead of:
echo "${env.PRODUCT_NAME}"
you can do:
echo.PRODUCT_NAME.
Additionally you can grab nested object values dynamically using bracket notation
def obj = [a: '1']
echo obj[a] // outputs '1'
Using these put together, you can do:
def prodName = env.PRODUCT_NAME //will set var prodName to "MyProdName"
echo env[prodName + '_Key'] //gets nested field with key "MyProdName_Key"
(Note: this is similar to Opal's answer, hopefully my breakdown helps)

Can I use two sets of variables in one foreach loop?

Is is possible to construct one single foreach loop that loops through using two separate sets of variables?
Below is a simplified example of what I'm trying to do - except this example lists two separate loops whereas I would like to set them up in one single loop.
$Sites = #("https://www.google.com" , "https://duckduckgo.com")
$Site_names = #( "Google" , "DuckDuckGO")
foreach ($element in $Sites) {
Write-Host "`n`n"
$element
Write-Host "`n`n"
}
foreach ($name in $Site_names) {
Write-Host "`n`n"
$name
Write-Host "`n`n"
}
There is other code to be used so the loop needs to be able to allow for multiple lines of code in the code block - so a single line solution if there is one isn't what I'm after. Also I didn't think using the pipeline would be workable (but I could certainly be wrong on that).
Two sets of variables: $Sites and $Site_names.
I would like one foreach loop that runs through and lists the site address and the site name with both values changing each time the loop is run.
First run: reference the URL "https://www.google.com" and the site name "Google".
Second run: reference the URL "https://duckduckgo.com" and the site name "DuckDuckGo".
Is this possible?
If you have two arrays of the same size you can simply use a for loop like this:
for ($i=0; $i -lt $Sites.Count; $i++) {
"{0}`t{1}" -f $Site_names[$i], $Sites[$i]
}
However, if the elements of your two arrays are correlated anyway, it would be better to use a hashtable instead:
$Sites = #{
'Google' = 'https://www.google.com'
'DuckDuckGo' = 'https://duckduckgo.com'
}
foreach ($name in $Sites.Keys) {
"{0}`t{1}" -f $name, $Sites[$name]
}

parse multilines from a file and replace

I need to read a file where the content is like below :
Computer Location = afp.local/EANG
Description = RED_TXT
Device Name = EANG04W
Domain Name = afp.local
Full Name = Admintech
Hardware Monitoring Type = ASIC2
Last Blocked Application Scan Date = 1420558125
Last Custom Definition Scan Date = 1348087114
Last Hardware Scan Date = 1420533869
Last Policy Sync Date = 1420533623
Last Software Scan Date = 1420533924
Last Update Scan Date = 1420558125
Last Vulnerability Scan Date = 1420558125
LDAP Location = **CN=EANG04W**,OU=EANG,DC=afp,DC=local
Login Name = ADMINTECH
Main Board OEM Name = Dell Inc.
Number of Files = 384091
Primary Owner = **CN= LOUHICHI anoir**,OU=EANG,DC=afp,DC=localenter code here
I need to replace CN=$value by CN=Compagny where $value is what is retrived after CN= and before ,.
Ok, so you really should have updated your question an not posted the code in a comment, because it's really hard to read. Here's what I think you intended:
$file = 'D:\sources\scripts\2.txt'
$content = Get-Content $file | foreach ($line in $content) {
if ($line.Contains('CN=')) {
$variable = $line.Split(',').Split('=')[2]
$variable1 = $variable -replace $variable, "Compagny"
} Set-Content -path $file
}
That deffinately has some syntax errors. The first line is great, you define the path. Then things go wrong... Your call to Get-Content is fine, that will get the contents of the file, and send them down the pipe.
You pipe that directly into a ForEach loop, but it's the wrong kind. What you really want there is a ForEach-Object loop (which can be confusing, because it can be shortened to just ForEach when used in a pipeline like this). The ForEach-Object loop does not declare an internal variable (such as ($line in $content)) and instead the scriptblock uses the automatic variable $_. So your loop needs to become something like:
Get-Content $file | ForEach { <do stuff> } | Set-Content
Next let's look inside that loop. You use an If statement to see if the line contains "CN=", understandable, and functional. If it does you then split the line on commas, and then again on equals, selecting the second record. Hm, you create an array of strings anytime you split one, and you have split a string twice, but only specify which record of the array you want to work with for the second split. That could be a problem. Anyway, you assign that substring to $variable, and proceed to replace that whole thing with "company" and store that output to $variable1. So there's a couple issues here. Once you split the string on the commas you have the following array of strings:
"LDAP Location = **CN=EANG04W**"
"OU=EANG"
"DC=afp"
"DC=local"
That's an array with 4 string objects. So then you try to split at least one of those (because you don't specify which one) on the equals sign. You now have an array with 4 array objects, where each of those has 2 string objects:
("LDAP Location", "**CN", "EANG04W**")
("OU", "EANG")
("DC","afp")
("DC","local")
You do specify the third record at this point (arrays in PowerShell start at record 0, so [2] specifies the third record). But you didn't specify which record in the first array so it's just going to throw errors. Let's say that you actually selected what you really wanted though, and I'm guessing that would be "EANG04W". (by the way, that would be $_.Split(",")[0].Split("=")[1]). You then assign that to $Variable, and proceed to replace all of it with "Company", so after PowerShell expands the variable it would look like this:
$variable1 = "EANG04W" -replace "EANG04W", "company"
Ok, you just successfully assigned "company" to a variable. And your If statement ends there. You never output anything from inside your If statement, so Set-Content has nothing to set. Also, it would set that nothing for each and every line that is piped to the ForEach statement, re-writing the file each time, but fortunately for you the script didn't work so it didn't erase your file. Plus, since you were trying to pipe to Set-Content, there was no output at the end of the pipeline, you have assigned absolutely nothing to $content.
So let's try and fix it, shall we? First line? Works great! No change. Now, we aren't saving anything in a variable, we just want to update a file's content, so there's no need to have $Content = there. We'll just move on then, shall we? We pipe the Get-Content into a ForEach loop, just like you tried to do. Once inside the ForEach loop, we're going to do things a bit differently though. The -replace method performs a RegEx match. We can use that to our advantage here. We will replace the text you are interested in for each line, and if it's not found, no replacement will be made, and pass each line on down the pipeline. That will look something like this for the inside of the ForEach:
$_ -replace "(<=CN\=).*?(?=,)", "Company"
The breakdown of that RegEx match can be seen here: https://regex101.com/r/gH6hP2/1
But, let's just say that it looks for text that has 'CN=' immediately before it, and goes up to the first comma following it. In your example, that includes the two trailing asterisks, but it doesn't touch the leading ones. Is that what you intended? That would make the last line of your example file:
Primary Owner = **CN=Company,OU=EANG,DC=afp,DC=localenter code here
Well, if that is as intended, then we have a winner. Now we close out the ForEach loop, and pipe the output to Set-Content and we're all set! Personally, I would highly suggest outputting to a new file, in case you need to reference the original file for some reason later, so that's what I'm going to do.
$file = 'D:\sources\scripts\2.txt'
$newfile = Join-Path (split-path $file) -ChildPath ('Updated-'+(split-path $file -Leaf))
Get-Content $file | ForEach{$_ -replace "(?<=CN\=).*?(?=,)", "Company"} | Set-Content $newfile
Ok, that's it. That code will produce D:\sources\scripts\Updated-2.txt with the following content:
Computer Location = afp.local/EANG
Description = RED_TXT
Device Name = EANG04W
Domain Name = afp.local
Full Name = Admintech
Hardware Monitoring Type = ASIC2
Last Blocked Application Scan Date = 1420558125
Last Custom Definition Scan Date = 1348087114
Last Hardware Scan Date = 1420533869
Last Policy Sync Date = 1420533623
Last Software Scan Date = 1420533924
Last Update Scan Date = 1420558125
Last Vulnerability Scan Date = 1420558125
LDAP Location = **CN=Company,OU=EANG,DC=afp,DC=local
Login Name = ADMINTECH
Main Board OEM Name = Dell Inc.
Number of Files = 384091
Primary Owner = **CN=Company,OU=EANG,DC=afp,DC=localenter code here

Resources