Unexpected Token in statement - PowerShell - powershell

I'm have a script which runs just fine at work but when I run the same script at home to build on it, the script fails with the following error:
Unexpected token '(' in expression or statement.
At C:\Users\MyAccount\Documents\Test.ps1:34 char:54
+ $Log = [Microsoft.VisualBasic.Interaction]::InputBox ('Enter the Even ...
Unexpected token '(' in expression or statement.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : UnexpectedToken
Here is the code which I am trying to run:
$log = [Microsoft.VisualBasic.Interaction]::InputBox ('Enter the Event ID you are looking for.', 'Event ID Query')
cd C:\temp\winevent_logs
$List = Get-ChildItem | Where-Object {$_.FullName -match "security"} | Select-Object
Get-WinEvent -FilterHashtable #{path=$List;id=$Log} | Measure-Object | Select-Object count
I'm guessing there's a configuration issue with my home system which is not interpreting Visual Basic operations, but I'm not sure where to start with this.

You have a syntax problem (which means that your problem is unrelated to which machine you run your code on):
When calling .NET methods in PowerShell, there must be no space between the method name and the opening parenthesis , (, unlike in C#, where this is allowed.
Using the [string] type's .ToUpper() instance method as a simple example:
# OK - no space between 'ToUpper' and '('
PS> 'foo'.ToUpper()
FOO
# BROKEN - space before '('
PS> 'foo'.ToUpper ()
ParserError:
Line |
1 | 'foo'.ToUpper ()
| ~
| Unexpected token '(' in expression or statement.
Note:
With the mistaken space, PowerShell interprets what follows the space ((), in this case) as a separate expression, which breaks the syntax, given that a space-separated list of expressions (that aren't connected with operators) is invalid; e.g., 1 2 triggers the same error.
As an aside: omitting () in what would otherwise be a method call - e.g., 'foo'.ToUpper - serves a useful purpose in PowerShell: it shows reflection information, namely the signatures (list of parameters and their types) of the method and its overloads; e.g.:
OverloadDefinitions
-------------------
string ToUpper()
string ToUpper(cultureinfo culture)

You put a space between InputBox and ('Enter. Remove the space so it gets executed as a method with arguments:
$log = [Microsoft.VisualBasic.Interaction]::InputBox('Enter the Event ID you are looking for.', 'Event ID Query')
Otherwise what you are trying to tell PowerShell to do is return the method object itself, and it doesn't know what to do with the resulting subexpression in parentheses.

Related

How to assign and reference environment variables containing square brackets in Powershell

When the PSDrive is not specified, the following works:
${[foo]}="bar"
echo ${[foo]}
But the following does not work
$env:${[foo]}="bar"
At line:1 char:1
+ $env:${[foo]}="bar"
+ ~~~~~
Variable reference is not valid. ':' was not followed by a valid variable name character. Consider using ${} to delimit the name.
At line:1 char:6
+ $env:${[foo]}="bar"
+ ~~~~~~~~~~~~~~
Unexpected token '${[foo]}="bar"' in expression or statement.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : InvalidVariableReferenceWithDrive
${env:[foo]}="bar"
Cannot find path 'env:[foo]' because it does not exist.
At line:1 char:1
+ ${env:[foo]}="bar"
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (env:[foo]:String) [], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound
The following works, though I am curious if there's short hand syntax for it:
Set-Item -LiteralPath env:${[foo]} -Value "bar"
Get-Item -LiteralPath env:${[foo]} | % {$_.Value}
However the following does not work:
Set-Item -LiteralPath env:${[foo]2} -Value "bar"
Set-Item : Cannot process argument because the value of argument "name" is null. Change the value of argument "name" to a non-null value.
At line:1 char:1
+ Set-Item -LiteralPath env:${[foo]2} -Value "bar"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:String) [Set-Item], PSArgumentNullException
+ FullyQualifiedErrorId : SetItemNullName,Microsoft.PowerShell.Commands.SetItemCommand
Written as of PowerShell Core 6.2.0
The reason is that PowerShell treats the following:
${<drive>:<name>}
as if you had specified:
Get-Content -Path <drive>:<name> # or, with assignment, Set-Content -Path ...
This notation - though often used with the Env: drive (e.g., $env:Path) - is little-known as a general paradigm named namespace variable notation, which is explained in this answer.
The problem is the use of -Path rather than -LiteralPath, because -Path interprets its argument as a wildcard expression.
Therefore, the [foo] in ${env:[foo]} - rather than being used as-is - is interpreted as a wildcard expression that matches a single character that is either f or o ([foo] is a character set or range ([...]) that matches any one of the (distinct) characters inside - see about_Wildcards).
On assigning to ${env:[foo]}, the logic of Set-Content -Path requires that a wildcard-based path resolve to something existing, even though you're generally not required to explicitly create environment variables; e.g., ${env:NoSuchVarExistsYet} = 'new' works just fine.
Workaround:
Use double(!)-`-escaping of the wildcard metacharacters:
# Namespace variable notation only works with if you
# double(!)-backtick-escape the wildcard metacharacters:
# Assign to / implicitly create env. var '[foo]'
${env:``[foo``]} = 'bar'
# Get its value.
${env:``[foo``]}
Note:
Escaping shouldn't be required at all, because there is no good reason to treat paths that conceptually identify a given, known item as wildcard expressions - see GitHub issue #9225.
That double `-escaping is needed is an added quirk - see GitHub issue #7999.
Another workaround - one that doesn't involve escaping - is to use
Set-Content -LiteralPath env:[foo] bar and Get-Content -LiteralPath env:[foo], but that is both verbose and slow.
As for the other syntax variations you tried:
$env:${[foo]}="bar"
Since your variable reference isn't {...}-enclosed as a whole (except for the initial $), the token that follows the : is only allowed to contain characters that do not require escaping - and $, { and } all violate that rule.
{...}-enclosing the entire path - ${env:[foo]} - solves the syntax problem, but runs into the problem detailed above.
Set-Item -LiteralPath env:${[foo]} -Value "bar"
This does not work in general, because string expansion is applied beforehand here - it is as if you had passed "env:${[foo]}": the reference to a (regular) variable named ${[foo]} is expanded (replaced with its value) and in effect appended to literal env:, before handing the result to Set-Item.
If such a regular variable doesn't exist, what Set-Item sees is just env: (because non-existent variables default to $null, which becomes the empty string in a string context), which causes an error due to the lack of variable name.
By contrast, the following would set an environment variable named unrelated instead:
# Create a regular variable literally named '[foo]'.
${[foo]} = 'unrelated'
# !! The following sets env:unrelated, i.e., env. var 'unrelated',
# !! due to the string expansion that is performed on the -LiteralPath
# !! argument up front.
Set-Item -LiteralPath env:${[foo]} bar
$env:unrelated # -> 'bar'
The same applies to Get-Item -LiteralPath env:${[foo]} and
Set-Item -LiteralPath env:${[foo]2} -Value "bar".

PowerShell if -like error

Not sure what's wrong with this script
$SSODelete= "Delete"
$SSOfiles = "SSo.xml"
$channlename = "INC8-Patch"
write-host $channlename
if ($channelname -like *INC8*){
$SSOarg = "$SSODelete $SSOfiles"
Write-Host $SSOarg
}
It throws following error:
You must provide a value expression following the '*' operator.
At line:5 char:25
+ if ($channelname -like *INC8*){
+ ~~~~~
Unexpected token 'INC8*' in expression or statement.
Tried using "*INC8*" and '*INC8' but it then it does not qualify if statement.
Adding screenshot for script and it's results
Simple Script and results
The PowerShell -like operator should be used with the matching string quoted, e.g., $variable -like "*pattern*". -like accepts the simple wildcard (*) only; you can compare against regular expressions (regexps) by using -match. See Get-Help about_Comparison_Operators.
Thanks , it was due to variable name i was comparing ... i need to be more cautious

How to provide Linux-style parameter names in a powershell script

I would like to create a Powershell script that takes parameters in the standard Linux style, i.e., --my-param, with a leading --. I thought this might be possible using the alias parameter attribute, as in
Param (
[parameter(Mandatory=$true)]
[alias("-my-param","p")]
[String]
$param
)
What I hoped with this was that the call
c:\src\ps\params.ps1 --my-param "x"
would be recognized as referring to the alias -my-param. Unfortunately, what I get is
C:\src\ps\params.ps1 : A positional parameter cannot be found that accepts argument 'x'.
At line:1 char:21
+ c:\src\ps\params.ps1 <<<< --my-param1 "x"
+ CategoryInfo : InvalidArgument: (:) [params.ps1], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,params.ps1
On the other hand, using the alias my-param in this lets me call the script with -my-param.
Is there a way of specifying parameter names with leading -- in Powershell?
Your syntax fails at tokenizer level. Compare:
[Management.Automation.PSParser]::Tokenize(
'Command -parameter',
[ref]$null
)
...and...
[Management.Automation.PSParser]::Tokenize(
'Command --parameter',
[ref]$null
)
As you can see former is seen by parser as parameter, latter - as argument.
So the only way would be parsing all arguments "internally" and guessing what is parameter (from your perspective), and what is argument.
I'm not aware of any libraries that will parse Unix-style parameters for you (which doesn't necessarily mean there isn't one...), but you could just not declare any parameters, and parse the parameters yourself in the body of the script.
This will create a hashtable of the parameters, where they keys are the parameter names and the values are the parameter values. Switch parameters will have null values.
$params = #{}
$MyInvocation.Line.Substring(($MyInvocation.Line.IndexOf('--') + 2)) -split ' --' | %{
$_ -match '(\S+) ?(.+)?' | Out-Null
$params.($matches[1]) = $matches[2]
}
$MyInvocation.Line gives you the command line that was used to invoke the script. $MyInvocation.Line.Substring(($MyInvocation.Line.IndexOf('--') + 2)) gives you everything following the first --.
$_ -match '(\S+) ?(.+)?' assigns the parameter name to the first match group, and the value to the second match group. The Out-Null prevents PowerShell from printing True for each iteration.
The reason I used (.+)? rather than (.*) is to make the values of switch parameters null. (.*) will match an empty string if there is nothing to match, making the value of $matches[2] an empty string, whereas (.+)? won't match, making $matches[2] null.
This is assuming that all parameters begin with --. If you want to allow a single hyphen, restrict single-dash parameter names to a single letter, or check for incorrectly declared parameters (for example throw an error if there's a triple-hyphen), you'll have to account for that in your code, but this is the basic idea.

pull text between characters

How do I pull text between two words? I know regex can do this and I have been looking around but the code I try just does not work for me at all...as clueless as a brick with regex...so probably am doing it totally wrong...
I have a text file and want to query whatever is displayed between these text strings:
[Problem Devices]
Device PNP Device ID Error Code
[USB]
I tried doing this but getting no where!
$devices = Get-Content c:\temp\dev.txt | out-string [regex]::match($devices,'(?<=\<Problem Devices\>).+(?=\<USB\>)',"singleline").value.trim()
You cannot call a method on a null-valued expression.
At line:1 char:141
+ $devices = Get-Content c:\temp\dev.txt | out-string [regex]::match($devices,'(?<=\<Problem Devices\>).+(?=\<USB\>)',"
singleline").value.trim <<<< ()
+ CategoryInfo : InvalidOperation: (trim:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Piping to out-string is not needed; get-content is sending each line of the file into the pipeline as a separate object. So you want to iterate through those objects with a foreach-object.
$devices = Get-Content c:\temp\dev.txt | foreach-object{[regex]::match($devices,'(?<=\<Problem Devices\>).+(?=\<USB\>)',"singleline").value.trim()}
However, you are still left with the problem of attempting to trim() a null object - if your regex match doesn't find a match, you can't call value.trim().
Your regex tries to match on <Problem Devices> when your input file has [Problem Devices].
Rather than try to do everything in a single set of pipeline steps, break your problem down:
For each line in the file, check for [Problem Devices]
For each subsequent line, if it is [USB], exit the loop. If it is not [USB], capturing each line into a variable (build an array of these lines)
After the loop, iterate over each element of the array you just built to parse each value out (creating a collection of PSObjects (one per device), or a collection of hashes (one per device), depending on your needs).
If you're not comfortable with regex, there are other ways:
$test = $false
$devices = get-content file.txt |
foreach {
if ($_.trim() -eq '[Problem Devices]'){$test = $true}
elseif ($_.trim() -eq '[USB]') {$test = $false}
elseif ($test){$_}
} | where {$_.trim()}

Why doesn't $hash.key syntax work inside the ExpandString method?

The following Powershell script demonstrates the issue:
$hash = #{'a' = 1; 'b' = 2}
Write-Host $hash['a'] # => 1
Write-Host $hash.a # => 1
# Two ways of printing using quoted strings.
Write-Host "$($hash['a'])" # => 1
Write-Host "$($hash.a)" # => 1
# And the same two ways Expanding a single-quoted string.
$ExecutionContext.InvokeCommand.ExpandString('$($hash[''a''])') # => 1
$ExecutionContext.InvokeCommand.ExpandString('$($hash.a)') # => Oh no!
Exception calling "ExpandString" with "1" argument(s): "Object reference not set to an instance of an object."
At line:1 char:1
+ $ExecutionContext.InvokeCommand.ExpandString('$($hash.a)')
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : NullReferenceException
Anyone know why the $hash.key syntax works everywhere but inside explicit expansion? Can this be fixed, or do I have to suck it up and live with the $hash[''key''] syntax?
I use this method, since this bug exists in v4 (not in v5)
function render() {
[CmdletBinding()]
param ( [parameter(ValueFromPipeline = $true)] [string] $str)
#buggy
#$ExecutionContext.InvokeCommand.ExpandString($str)
"#`"`n$str`n`"#" | iex
}
Usage for your example:
'$($hash.a)' | render
The ExpandString api is not exactly meant for use from PowerShell scripts, it was added more for C# code. It's still a bug that your example doesn't work (and I think it's been fixed in V4), but it does mean there is a workaround - one that I recommend for general use.
Double quoted strings effectively (but not literally) call ExpandString. So the following should be equivalent:
$ExecutionContext.InvokeCommand.ExpandString('$($hash.a)')
"$($hash.a)"
I was trying to store text that prompts the user in a text file. I wanted to be able to have variables in the text file that are expanded from my script.
My settings are stored in a PSCustomObject called $profile and so in my text I was trying to do something like:
Hello $($profile.First) $($profile.Last)!!!
and then from my script I was trying to do:
$profile=GetProfile #Function returns PSCustomObject
$temp=Get-Content -Path "myFile.txt"
$myText=Join-String $temp
$myText=$ExecutionContext.InvokeCommand.ExpandString($myText)
which of course left me with the error
Exception calling "ExpandString" with "1" argument(s): "Object
reference not set to an instance of an object."
Finally I figured out I only needed to to store the PSCustomObject values I want in regular old variables, change the text file to use those instead of the object.property version and everything worked nicely:
$profile=GetProfile #Function returns PSCustomObject
$First=$profile.First
$Last=$profile.Last
$temp=Get-Content -Path "myFile.txt"
$myText=Join-String $temp
$myText=$ExecutionContext.InvokeCommand.ExpandString($myText)
And in the text I changed to
Hello $First $Last!!!