Powershell DataTable,Select Filter does not accept single quote character - powershell

I'm writing a simple Name search against a datatable using Powershell. The code as following:
$filter = "Name = '$aContact'"
$arow = $dt_resources.Select($filter)
This works all fine except if $aContact contains a single quote (') character, like "O'Connor".
Any ideal how can I workaround this issue?
Thanks all in advance.

Mathias R. Jessen already commented on the solution to the question but to put it as an answer, you can escape the ' by doubling them. I added a simple function that will also serve as workaround for possible $null input since both methods (.Contains and .Replace) would throw an exception in that case.
$dtt = [System.Data.DataTable]::new()
'Name', 'Value' | ForEach-Object{
$dtt.Columns.Add([System.Data.DataColumn]::new($_))
}
$row = $dtt.NewRow()
$row.Name = "O'Connor"
$row.Value = [random]::new().Next()
$dtt.Rows.Add($row)
$row = $dtt.NewRow()
$row.Name = "Doe"
$row.Value = [random]::new().Next()
$dtt.Rows.Add($row)
function CheckQuery {
param(
[parameter(ValueFromPipeline)]
[string]$Value
)
if($Value.Contains("'")) { return $Value.Replace("'","''") }
return $Value
}
"O'Connor", "Doe", $null | ForEach-Object {
$filter = "Name = '{0}'" -f ($_ | CheckQuery)
$dtt.Select($filter)
}

Related

Get-ADUser - want to write only one part of the OU into a variable

I have this:
Get-ADUser myuser |
Select #{n='OU';e={$_.DistinguishedName -replace '^.*?,(?=[A-Z]{2}=)'}}
But I need to get only on part of the OU of a specific user which I have to define as a variable in the beginning.
I get this
OU=Users,OU=Munich,DC=xyzdom,DC=xyz
And I want to detect if the user is in the Munich OU or where ever.
So the output should be just $city and the input $username
I have no clue how to do this. But I suspect it should be not as hard to achieve this goal.
Maybe someone has time and passion to show me how :)
Thank you so much
Greetings
Thanks a lot for the help. (I can't use the city property.) My solution looks like this now:
Import-Module ActiveDirectory
$samaccountname = "Smith"
$ou = Get-ADUser $samaccountname | Select #{n='OU';e={$_.DistinguishedName.split(',')[-3].split("=")[-1]}} | FT -HideTableHeaders
$ou
Now, the output is just: Munich
I want to go on using this variable but maybe it's in a wrong format. when I try to use it with orchestrator I get an output like this: Microsoft.PowerShell.Commands.Internal.Format.FormatStartData Microsoft.PowerShell.Commands.Internal.Format.GroupStartData Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData Microsoft.PowerShell.Commands.Internal.Format.GroupEndData Microsoft.PowerShell.Commands.Internal.Format.FormatEndData
So maybe it has to be formated as string??? How can I do that?
I agree with Santiago that using the users AD attribute City would be a much better solution, but if you don't have that filled in on the users, you may try below.
A DistinguishedName can contain commas, escaped characters and even special characters converted to their HEX representation.
See here and there
Simply splitting a DN on the comma can therefore return unwanted results.
For this, I've written a small helper function some time ago you could use:
function Parse-DistinghuishedName {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)]
[string[]]$DistinghuishedName
)
begin {
function _ReplaceSpecial([string]$value) {
# replace all special characters formatted as BackSlash-TwoDigitHexCode
$match = ([regex]'(?i)\\([0-9a-f]{2})').Match($value)
while ($match.Success) {
$value = $value -replace "\\$($match.Groups[1].Value)", [char][convert]::ToUInt16($match.Groups[1].Value, 16)
$match = $match.NextMatch()
}
# finally, replace all backslash escaped characters
$value -replace '\\(.)', '$1'
}
}
process {
foreach ($dn in $DistinghuishedName) {
$hash = [ordered]#{}
# split the string into separate RDN (RelativeDistinguishedName) components
$dn -split ',\s*(?<!\\,\s*)' | ForEach-Object {
$name, $value = ($_ -split '=', 2).Trim()
if (![string]::IsNullOrWhiteSpace($value)) {
$value = _ReplaceSpecial $value
switch ($name) {
'O' { $hash['Organization'] = $value }
'L' { $hash['City'] = $value }
'S' { $hash['State'] = $value }
'C' { $hash['Country'] = $value }
'ST' { $hash['StateOrProvince'] = $value }
'UID' { $hash['UserId'] = $value }
'STREET' { $hash['Street'] = $value }
# these RDN's can occur multiple times, so add as arrays
'CN' { $hash['Name'] += #($value) }
'OU' { $hash['OrganizationalUnit'] += #($value) }
'DC' { $hash['DomainComponent'] += #($value) }
}
}
}
$hash
}
}
}
It parses the DN into its RDN components and returns a Hashtable.
In your case, use it like:
(Parse-DistinghuishedName 'OU=Users,OU=Munich,DC=xyzdom,DC=xyz').OrganizationalUnit[1] # --> Munich

Powershell Most efficient way to combine customobject

I'm parsing a webpage, but having difficulty combining into one variable.
I'm looking for the most efficient way to do so as well.
This is code I have so far. Any help is appreciated.
$WebResponse = Invoke-WebRequest -Uri "https://finviz.com/news.ashx"
$title = ($WebResponse.AllElements | Where {$_.class -match 'nn-tab-link'}).innertext
$time = ($WebResponse.AllElements | Where {$_.class -match 'nn-date'}).innertext
$link = ($WebResponse.AllElements | Where {$_.class -match 'nn-tab-link'}).href
$r = {
[PSCustomObject]#{
{Time(E/T)} = $time
Headline = $title
Link = $link
}
}
$R
Thanks
Use an index-based loop (which assumes that all three arrays have corresponding elements and the same element count):
$objects = foreach ($i in 0..($title.Count-1)) {
[pscustomobject] #{
'Time(E/T)' = $time[$i]
Headline = $title[$i]
Link = $link[$i]
}
}
Note how property name Time(E/T) is enclosed in '...' - a verbatim string - rather than in {...} - a script block; the latter only works accidentally, because script blocks stringify[1] to their verbatim content (without the { and }).
[1] When using the [pscustomobject] #{ ... } syntactic sugar, the keys of the hashtable (#{ ... }) are implicitly stringified, given that objects' property names are invariably strings.

PowerShell is not splitting param

I have a PowerShell script (below) which accepts an argument (string) and then splits it. For some reason, the code below doesn't work unless I use an intermediate variable like so. My question - why is that the case?
$name2 = $name.Split(" ")
function New-Name {
param(
[parameter(Mandatory=$True)]
[string] $name
)
$name = $name.Split(" ")
Write-Debug $name
if( $name.Count -gt 1 ) {
Write-Debug "2+"
}
else {
Write-Debug "1"
}
}
It's happening because you're declaring $name as string but $name.Split(" ") returns an array.
As cbaconnier says: .Split() returns an array of substrings.
For instance, if you have:
$name = 'John Doe'
$firstname = $name.Split(" ")[0] # returns "John"
$lastname = $name.Split(" ")[1] # returns "Doe"
Instead of using the .Net .Split() method, you could also do the PowerShell -split which uses regular expression and has the advantage of doing the above in one go:
$name = 'John Doe'
$firstname, $lastname = $name -split ' '
Have a look at the String.Split Method for the many overloads you can use and also look at About Split.
Hope that explains #cbaconnier answer

Powershell - if $var -like $var*

I'm working with SCSM in Powershell but running into an issue with an if statement.
I have a function that collects data based on a criteria that is passed into the function as a variable.
Example:
$JMLs1 = collectTickets -crit $JMLCriteria1
collectTickets is the function, $JMLCriteria1 is the criteria that is passed.
This is the collectTickets function:
function collectTickets
{
param (
[parameter (Mandatory=$true)]
$crit
)
$fullDate = Get-Date
if ($fullDate.DayOfWeek -eq 'Monday')
{
$olderThan = $fullDate.AddDays(-4)
}
elseif ($fullDate.DayOfWeek -eq 'Tuesday')
{
$olderThan = $fullDate.AddDays(-4)
}
else
{
$olderThan = $fullDate.AddDays(-2)
}
if ($crit -like '$JML*')
{
$data = Get-SCSMObject -Criteria $crit | select 'Id', 'Title', 'LastModified', 'Priority'
}
else
{
$data = Get-SCSMObject -Criteria $crit | Where-Object {($_.LastModified -lt $olderThan)} | select 'Id', 'Title', 'LastModified', 'Priority'
}
return $data
}
The issue I'm having is with the second if statement, the if ($crit -like '$JML*') - I'm not sure if we can use wildcards like this against variables or if the syntax is just not correct.
Just to clarify there will be multiple $JML criteria variables as well as multiple other criteria variables, but it's only the $JML criteria variables that I want to treat differently.
Use double quotes instead of single. With single quotes, PowerShell thinks you are looking for the the literal string $JML, not the variable.
if ($crit -like "$JML*")
Edit: about_Quoting documentation

Is it possible to include functions only without executing the script?

Say I have MyScript.ps1:
[cmdletbinding()]
param (
[Parameter(Mandatory=$true)]
[string] $MyInput
)
function Show-Input {
param ([string] $Incoming)
Write-Output $Incoming
}
function Save-TheWorld {
#ToDo
}
Write-Host (Show-Input $MyInput)
Is it possible to dot source the functions only somehow? The problem is that if the script above is dot sourced, it executes the whole thing...
Is my best option to use Get-Content and parse out the functions and use Invoke-Expression...? Or is there a way to access PowerShell's parser programmatically? I see this might be possible with PSv3 using [System.Management.Automation.Language.Parser]::ParseInput but this isn't an option because it has to work on PSv2.
The reason why I'm asking is that i'm trying out the Pester PowerShell unit testing framework and the way it runs tests on functions is by dot sourcing the file with the functions in the test fixture. The test fixture looks like this:
MyScript.Tests.ps1
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".")
. "$here\$sut"
Describe "Show-Input" {
It "Verifies input 'Hello' is equal to output 'Hello'" {
$output = Show-Input "Hello"
$output.should.be("Hello")
}
}
Using Doug's Get-Function function you could include the functions this way:
$script = get-item .\myscript.ps1
foreach ($function in (get-function $script))
{
$startline = $function.line - 1
$endline = $startline
$successful = $false
while (! $successful)
{
try {
$partialfunction = ((get-content $script)[$startline..$endline]) -join [environment]::newline
invoke-expression $partialfunction
$successful = $true
}
catch [Exception] { $endline++ }
}
}
Edit: [System.Management.Automation.IncompleteParseException] can be used instead of [Exception] in Powershell V2.
Note -- if you find this answer helpful please upvote jonZ's answer as I wouldn't of been able to come up with this if it weren't for his helpful answer.
I created this function extractor function based on the script #jonZ linked to. This uses [System.Management.Automation.PsParser]::Tokenize to traverse all tokens in the input script and parses out functions into function info objects and returns all function info objects as an array. Each object looks like this:
Start : 99
Stop : 182
StartLine : 7
Name : Show-Input
StopLine : 10
StartColumn : 5
StopColumn : 1
Text : {function Show-Input {, param ([string] $Incoming), Write-Output $Incoming, }}
The text property is a string array and can be written to temporary file and dot sourced in or combined into a string using a newline and imported using Invoke-Expression.
Only the function text is extracted so if a line has multiple statements such as: Get-Process ; function foo () { only the part relevant to the function will be extracted.
function Get-Functions {
param (
[Parameter(Mandatory=$true)]
[System.IO.FileInfo] $File
)
try {
$content = Get-Content $File
$PSTokens = [System.Management.Automation.PsParser]::Tokenize($content, [ref] $null)
$functions = #()
#Traverse tokens.
for ($i = 0; $i -lt $PSTokens.Count; $i++) {
if($PSTokens[$i].Type -eq 'Keyword' -and $PSTokens[$i].Content -eq 'Function' ) {
$fxStart = $PSTokens[$i].Start
$fxStartLine = $PSTokens[$i].StartLine
$fxStartCol = $PSTokens[$i].StartColumn
#Skip to the function name.
while (-not ($PSTokens[$i].Type -eq 'CommandArgument')) {$i++}
$functionName = $PSTokens[$i].Content
#Skip to the start of the function body.
while (-not ($PSTokens[$i].Type -eq 'GroupStart') -and -not ($PSTokens[$i].Content -eq '{')) {$i++ }
#Skip to the closing brace.
$startCount = 1
while ($startCount -gt 0) { $i++
if ($PSTokens[$i].Type -eq 'GroupStart' -and $PSTokens[$i].Content -eq '{') {$startCount++}
if ($PSTokens[$i].Type -eq 'GroupEnd' -and $PSTokens[$i].Content -eq '}') {$startCount--}
}
$fxStop = $PSTokens[$i].Start
$fxStopLine = $PSTokens[$i].StartLine
$fxStopCol = $PSTokens[$i].StartColumn
#Extract function text. Handle 1 line functions.
$fxText = $content[($fxStartLine -1)..($fxStopLine -1)]
$origLine = $fxText[0]
$fxText[0] = $fxText[0].Substring(($fxStartCol -1), $fxText[0].Length - ($fxStartCol -1))
if ($fxText[0] -eq $fxText[-1]) {
$fxText[-1] = $fxText[-1].Substring(0, ($fxStopCol - ($origLine.Length - $fxText[0].Length)))
} else {
$fxText[-1] = $fxText[-1].Substring(0, ($fxStopCol))
}
$fxInfo = New-Object -TypeName PsObject -Property #{
Name = $functionName
Start = $fxStart
StartLine = $fxStartLine
StartColumn = $fxStartCol
Stop = $fxStop
StopLine = $fxStopLine
StopColumn = $fxStopCol
Text = $fxText
}
$functions += $fxInfo
}
}
return $functions
} catch {
throw "Failed in parse file '{0}'. The error was '{1}'." -f $File, $_
}
}
# Dumping to file and dot sourcing:
Get-Functions -File C:\MyScript.ps1 | Select -ExpandProperty Text | Out-File C:\fxs.ps1
. C:\fxs.ps1
Show-Input "hi"
#Or import without dumping to file:
Get-Functions -File C:\MyScript.ps1 | % {
$_.Text -join [Environment]::NewLine | Invoke-Expression
}
Show-Input "hi"