I've come across a weird "bug" or foible in PowerShell, trying to split a string on "(". Can anyone tell me what's going on, and if there is an easy work-around?
Here's the code:
$description = 'Get-ParsedData($Data)'
$description -split "("
Result:
parsing "(" - Not enough )'s.
At line:1 char:1
+ $description -split "("
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], ArgumentException
+ FullyQualifiedErrorId : System.ArgumentException
I've tried using '(' as well as "(", and also
$description -split [char]0x0028
All result in the same error message: parsing "(" - Not enough )'s.
In the end I got around the problem with the following code, which works:
$description.SubString(0, $description.IndexOf('('))
However, I'm still curious as to why I was getting the original error and whether there is a simple work-around.
-split is a regular expression operator, and ( needs to be escaped (\():
$description -split "\("
The error message "Not enough )'s" might seem strange at first, but the reason ( needs to be escaped in regular expressions is that parentheses are used for grouping constructs:
PS C:\> 'abc' -split '(b)'
a
b
c
In the example above, we split on b, but "capture" it's value by enclosing it in ().
So when you pass the string "(" as a pattern, the regex engine sees it and goes "that ( is the start of a capture group", and since it can't find a corresponding ), it throws that error.
You can also use the [regex]::Escape() method to automatically escape any literal character in a regex pattern:
$splitPattern = [regex]::Escape("(")
$description -split $splitPattern
Alternatively, use the String.Split() method which only does simple substring replacement (and ( therefore doesn't need escaping):
$description.Split("(")
Related
I have a string : $row.TableName_Org[$i]
The value it contains is
This is a happy little asterisk: '*'
Now I want to do an IF based on the fact that the string contains an asterisk.
if($row.TableName_Org[$i] -Match "*") {
//Do Something
}
However gives me this error:
"*" - Kwantiteitsmeter {x,y} wordt door niets voorafgegaan. parseren
At C:\Users\hveijer\VS Code Repos\migratie-uitwissel\ReadData.ps1:33 char:4
+ $row.TableName_Org[$i] -match "*"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], ArgumentException
+ FullyQualifiedErrorId : System.ArgumentException
As you've found out yourself, the escape character in PowerShells wildcard/glob mechanism is ` (backtick):
'string with * in it' -like '`*'
... but the backtick is also the escape character for expandable (double-quoted) strings, leading to situations with awkward double escaping, like:
$pattern = "${prefix}``*"
For this reason, I prefer to let PowerShell escape my search terms for me instead of doing it manually:
[wildcardpattern]::Escape("${prefix}*")
Turns out I had to escape the * using ` (slash backtick)
Why can I use split as a method and command line switch, but not join? How do I discover what flags (e.g. -join) an object supports?
> "a,b,c,d" -split ','
a
b
c
d
> "a,b,c,d".split(',')
a
b
c
d
> "a,b,c,d".split(',').join(';')
Method invocation failed because [System.String] does not contain a method named 'join'.
At line:1 char:1
+ "a,b,c,d".split(',').join(';')
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MethodNotFound
> "a,b,c,d".split(',') -join ';'
a;b;c;d
Why can I use split as a method and command line switch, but not join?
Because String objects have a method Split(), but arrays don't have a method Join(), whereas -split and -join are operators provided by PowerShell.
The String class does have a (static) Join() method complementing Split(), though. You use it like this:
[String]::Join(',', ("a,b,c,d" -split ','))
Another thing you could do is set the output field separator ($OFS) to your delimiter character and embed your array in a string:
$OFS = ','
"$("a,b,c,d" -split ',')"
BTW, -split and Split() don't work the same way, so don't confuse them. The former uses a regular expression, the latter a character array.
PS C:\> 'a b' -split '\s+'
a
b
PS C:\> 'a b'.Split('\s+')
a b
PS C:\> 'a,b;c' -split (',', ';')
Cannot convert value ";" to type "System.Int32". Error: "Input string was not in
a correct format."
At line:1 char:9
+ 'a,b;c' -split (',', ';')
+ ~~~~~~
+ CategoryInfo : NotSpecified: (:) [], RuntimeException
+ FullyQualifiedErrorId : RuntimeException
PS C:\> 'a,b;c'.Split((',', ';'))
a
b
c
How do I discover what flags (e.g. -join) an object supports?
By reading the documentation. -join isn't a method, flag, or commandline switch. It's a PowerShell operator.
If you will do:
"String" | Get-Member
you will see that there is no Join Method available, only Split().
You can Use however the -Join Operator instead, which exist for -Split as well
In your Example:
"a,b,c,d".split(',') -join ";"
or Use the: [string]::Join() Class
I'm trying to take the input "name" and find the whitespace so that I can find the first and last letter of the first and last name entered. But it's telling me "You cannot call a method on a null-valued expression." Help?
$name = Read-Host "Please enter the zip of person $count"
$length = $name.Length
$name = $name.split(" ")
write-host $name[0]
You made a significant change to your post. It used to be:
$name = Read-Host "Please enter the name of person $count"
$length = $name.Length
$pos = $name.IndexOf('\s')
print $pos
Lets ignore the $length line since you do not use it. It looks like you are trying to split on the first white space using regular expressions. You are using .IndexOf() which if you examine the overloads will see it expects strings or a char. Not earth shattering but the point is it is looking for string literals and does not support regular expressions. Consider the following statements
PS C:\Users\Cameron> "John Smith".IndexOf('\s')
-1
PS C:\Users\Cameron> "John\sSmith".IndexOf('\s')
4
The first returned -1 since the string \s was not found. Then we put that string between John and Smith and we now get a positive return since the string literal was matched.
What you were possibly trying to do was use the -split which I base on your original question and the edit. -split supports regular expressions.
PS C:\Users\Cameron> $name = "John Smith"
PS C:\Users\Cameron> ($name -split '\s+')[0]
John
What the second line of code did was split the string $name on the group of white-space. We returned the first element which would be 'John'. The second, not shown since it should be obvious is 'Smith'. Neither elements have trailing or leading white-space ( based only on this example. Mileage will vary on other strings.)
Also, the command print is and alias for Out-Printer. Doubt you meant that command and were most likely looking for, like Dane Boulton suggested, Write-Host or Write-Output. Note: Look them up to understand an important difference. Former writes to console where the ladder writes to the output stream.
Lastly good guess would be that the error was caused by $name being null at the time $name.IndexOf('\s') was called. Easy to simulate as well.
PS C:\Users\Cameron> $something = $null
PS C:\Users\Cameron> $something.Method()
You cannot call a method on a null-valued expression.
At line:1 char:1
+ $something.Method()
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
im not sure which is causing you to get the null value error. But to get the first and last name just use this:
$name = "John Smith"
$name = $name.split(" ")
$name[0] #outputs John
$name[1] #outputs Smith
Also you want write-host or write-output not print
This is a simplified version of my function:
function DetectLocalUser($localGroup, $members)
{
$result = net localgroup "$localGroup"
#$members= $members.Replace("\","\\")
if ($result -match $members)
{
return $true
}
else
{
return $false
}
}
To invoke the function I use this example (Typical values I am going to receive):
DETECTLocalUser "test" "iis apppool\userapi"
The parameters are not controlled by me. If they were I would escape directly the second parameter "iis apppool\\userapi"
On execution I have a problem with the \ in the parameter. The exact error is:
parsing "iis apppool\icisapi" - Unrecognized escape sequence \i. At
C:\k\a.ps1:6 char:9
+ if ($result -match $members)
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], ArgumentException
+ FullyQualifiedErrorId : System.ArgumentException
I found a workaround by adding #$members= $members.Replace("\","\\") fixes the problem but I am not sure if is the best option.
Is my workaroud acceptable or is there a better way of escaping $members parameter?
[RegEx]::Escape($members)
That will ensure that characters in strings get interpreted as literals and not as part of the RegEx.
To explain further, the -match operator is doing a regular expression match, so the string you pass to it is interpreted as a regular expression.
Backslash happens to be the escape character in a regular expression, so that's where your issue is. Using [RegEx]::Escape() ensures that other characters won't be interpreted, such as [,],.,+,(,),^,$,?,*, etc.
I'm a beginner in powershell and know C# pretty well. I have this command http://www.f2ko.de/programs.php?lang=en&pid=cmd that downloads stuff. I'm writing this script to download all the sgf go games from this url http://www.gogameworld.com/gophp/pg_samplegames.php, and was trying to write a powershell script to do it for me. So I wrote a script:
Get-Content test.txt|
ForEach-Object
{
if($_ -eq "=`"javascript:viewdemogame(`'*.sgf`')`" tit")
{
$filename = $_ -replace '=`"javascript:viewdemogame(`''
$filename = $filename -replace '`')`" tit'
&"(Path)/download.exe" ("http://www.gogameworld.com/webclient/qipu/" + $filename)
}
}
However, when I run the script, I keep getting this error:
Unexpected token '`'' in expression or statement.
At (PATH)\test.ps1:7 char:37
+ $filename = $filename -replace '`' <<<< )'
+ CategoryInfo : ParserError: (`':String) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : UnexpectedToken
I've looked at the script lots of times and still can't figure out whats wrong. Thanks.
Try this, read the content of the file as one string and then use the Regex.Matches to get all occurrences of the text contained in the parenthesis:
$content = Get-Content test.txt | Out-String
$baseUrl = 'http://www.gogameworld.com/webclient/qipu/'
[regex]::matches($content,"javascript:viewdemogame\('([^\']+)'\)") | Foreach-Object{
$url = '{0}{1}' -f $baseUrl,$_.Groups[1].Value
& "(Path)/download.exe" $url
}
here's an explanation of the regex pattern (created with RegexBuddy):
javascript:viewdemogame\('([^\']+)'\)
Match the characters “javascript:viewdemogame” literally «javascript:viewdemogame»
Match the character “(” literally «\(»
Match the character “'” literally «'»
Match the regular expression below and capture its match into backreference number 1 «([^\']+)»
Match any character that is NOT a ' character «[^\']+»
Between one and unlimited times, as many times as possible, giving back as needed (greedy) «+»
Match the character “'” literally «'»
Match the character “)” literally «\)»
Match the character “"” literally «"»
'{0}{1}' is used with the -f operator to create a string. {0} maps to the first value on the right hand side of the operator (e.g $baseUrl) and {1} is mapped to the second value. Under the hood, PowerShell is suing the .NET String.Format method. You can read more about it here: http://devcentral.f5.com/weblogs/Joe/archive/2008/12/19/powershell-abcs---f-is-for-format-operator.aspx
'')" tit'
The -replace operator takes 2 arguments, comma separated. The first is a regular expression that matches what you want replaced. The second is the string you want to relace that with. You appear to be missing the second argument.