This is just a question of curiosity to better learn how PowerShell works. Why can I put the repeating string into a variable, but not put it in a Write-Host statement? The purpose is to print a row of dashes as a divider.
$divider = "-"*25
Write-Host $divider
Write-Host "-"*25
Write-Host $("-"*25) #is this the best way to do it with a var?
Results Window:
-------------------------
- *25
-------------------------
In, Write-Host "-"*25, it is evaluated as an argument to the function instead of an expression. So, parenthesis need to be used to get it as an expression first and then result of this expression passed as an argument to Write-Host.
Check the about Parsing
Related
When I run the backtick (`) command on this snippet:
Get-WmiObject win32_service | Where-Object { $_.pathname -notlike "C:\windows\*" -and $_.startmode -eq "auto" -and $_.startname -eq "localsystem"} | Select-Object displayname, `
pathname, startmode, startname | Format-List | Out-Host
I get some errors. What are those errors?
First, by pressing F8 just on the first line, I get this:
Incomplete string token.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : IncompleteString
Objective: Running the snippet and assuming the PC will jump onto the next line automatically, since it has the ` character.
Second, when I highlight just the first line, by clicking to the left of the line numbers, I now get this:
At line:1 char:170
... to" -and $_.startname -eq "localsystem"}|Select-Object displayname, `
~ Missing expression after ',' > in pipeline element.
CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
FullyQualifiedErrorId : MissingExpression
Objective: Running the snippet and assuming the PC will jump onto the next line automatically, since it has the ` character.
However, when I press F5 it works like a charm. Please forgive my ignorance on PowerShell, but what am I doing wrong here?
Additional info:
This is my powershell information:
Name: Windows PowerShell ISE Host
Version: 5.1
Your errors are coming from features of PowerShell ISE.
First you're trying to use F8 which is "Run Selection", on the first line, with no selection made. That will implicitly select all the characters of the first line, then try to run that.
When you do so, you get the incomplete string token error, and that's because the parser has encountered the lone backtick ` (the escape character), with no character following it. That's because the newline at the end of the line, which the backtick is usually escaping (that's how it works as a line continuation character), is missing, since the single line selection didn't include it.
Second, when I highlight just the first line, by clicking to the left of the line numbers
Now in this case you'll notice that your selection has placed the cursor at the beginning of the next line. That means this selection does include the newline, and so you now have a complete string token.
Your error is that you've now ended your command with a comma, as though you're going to pass more parameters, and then nothing comes after (the rest of the parameters were not included in your selection).
The root of the issue is that the commands in ISE that deal with running a selection, are doing exactly that, so if you want them to include things that are on the next line, you must include them in the selection too.
As a side note, I might recommend that you look for code elements which let you naturally use line breaks, such as the pipe | character, operators, and scriptblock braces {}.
Get-WmiObject win32_service |
Where-Object {
$_.pathname -notlike "C:\windows\*" -and
$_.startmode -eq "auto" -and
$_.startname -eq "localsystem"
} |
Select-Object displayname, pathname, startmode, startname |
Format-List |
Out-Host
This doesn't solve your selection problem, but it's nicer to read in my opinion, and doesn't require the awkward backtick.
Following up on #Santiagos link comment:
This is referred to as Statement Termination. When using Powershell, there are 2 statement terminator charcters.
Semicolon - ;
Newline (sometimes)
The rule for the newline is only sometimes due to the nature of how some users will use commands. Basically meaning that if the previous text is syntactically a complete statement, a new line is considered to be a statement termination. So, if it isn't complete, the newline is treated as whitespace.
Quick example:
PS C:\Users\Abraham> 3 +
>> 4
7
When I added the + operator, it was expecting another argument which is why it didn't error out.
In your case, I will assume the error came from powershells Tokenizer (lexical analyzer) when parsing through your command, as it read your backtick as an escape character for your comma (assuming it was complete - hence the statement terminator - reading the next line as a newline and not whitespace); in my opinion ( and someone correct me if im wrong ), I think it was a bug which was a False-Positive.
The Backtik (`) also known as the Escape Character can extend a line that isn't extensible. If the last character in the line is a backtick, then the newline will be treated as whitespace and not a "newline".
So you don't need that backtick due to your comma (,) telling powershell that theres more that come after it, which we reference back to the 2 paragraph for being syntactically incomplete.
In summary: it was a mistake on powershells end(:
PS - Sorry about the long post, since the first part isn't really needed to answer your question; just some info that can help you understand how it comes together.
EDIT:
Didn't see the F8 being the culprit here lol thanks to #briantist for pointing it out. Error wasn't on Powershells end. Disregard this post (:
I'm trying to pass a variable to this command in PowerShell
Start-Process $devconloc -ArgumentList "\disable" '"'$GPUList[1]'"' -ErrorAction Stop
$GPUList[0] is a hardware ID and needs to be passed to devcon.exe in quotes:
"PCI\VEN_10DE&DEV_1CBB&SUBSYS_087D1028&REV_A1\4&44A1B07&0&0008"
But I get the following error
Start-Process : A positional parameter cannot be found that accepts argument '"'.
Any ideas what is happening?
Like the others allready pointed out in the comments, there are two problems here:
You should pass a string-array to -ArgumentList. You do this by seperating you arguments with a ,. Not a whitespace.
To fill in the object $GPUList[1] correct with your " around the string, there are two ways to fill the string with object:
Escaping the " in the string with ` and phrasing the variables in () to make sure the array position will be taken in notice : -ArgumentList '\disable', `"$($GPUList[1])`"
Filling in the variable with a position reference: -ArgumentList 'disable', ('"{0}"' -f $GPUList[1])
I'm returning to powershell from bash after a long time, and I've found the where object behavior to be quite confusing.
Why does the following snippet return success? Nothing is found! Why does this not return failure like a grep would?
C:> Get-Process | ?{$_.name -like "laksdjfajsdfkjasdkf"}
C:> echo $?
True
tl;dr
# Run the command and, in addition to outputting to the console,
# collect the results in variable $result, via common parameter -OutVariable / -ov
# If you do NOT need to output to the console, simply use:
# $result = Get-Process | ...
Get-Process | ? { $_.name -like "laksdjfajsdfkjasdkf" } -ov result
# Test if the result is empty (using implicit Boolean conversion)
if (-not $result) { Write-Warning "Nothing matched." }
PowerShell's automatic (Boolean) $? variable in PowerShell is not the (abstract) equivalent of exit codes in traditional shells, as PetSerAl points out.
$? just tells you whether the last statement succeeded and the rules surrounding it are complicated, as the GitHub discussion that Owain Esau links to shows.
Succeeded means that no errors occurred, and a filtering operation not returning anything is success by that definition.
In short: $? is of limited usefulness in PowerShell.
However, the exit code of the most recently executed external program is reflected in automatic variable $LASTEXITCODE, so had you actually invoked grep, its exit code would be reflected there.
(And while $? is set immediately after execution of an external program to reflect $True if the exit code was 0 and $False otherwise, $? may already reflect something else by the time the statement finishes, depending on the specifics of the statement, such as enclosing the call in (...))
In the case at hand you're looking to determine whether the filtering operation performed by the call to the Where-Object cmdlet (invoked via its built-in alias ?) returned any matches, but in PowerShell that status is not reflected anywhere separately.
Therefore, you must examine the output itself to determine whether anything matched, as shown in the snippet at the top.
There are no errors in this scenario, but for the sake of completeness:
PowerShell's error handling is sophisticated, but complex, and again unlike that of traditional shells; you can find an overview here.
$a = [char]65
$w1 = New-Object -ComObject wscript.shell;
$w1.AppActivate('Untitled - Notepad')
Sleep 1
Write-Host $a
$w1.SendKeys($a);
In a PowerShell script I am trying to use the SendKeys command to make the letter A appear in Notepad by typecasting the variable $a as a [char] with the value of 65. This gives me the expected result when I run the line:
Write-Host $a
(A appears in PowerShell)
But whenever the following line runs:
$w1.SendKeys($a);
The numbers 65 are put into Notepad instead of A.
Why do these two lines produce different outputs when given the same parameter and what can I do to make the variable $a output the character A in both situations?
Because char is an integral type, SendKeys is treating $a as an integer. You can get the same behavior by passing in 65 directly:
$w1.SendKeys(65); # Sends "65" to Notepad
To fix the problem, you need to explicitly give SendKeys a string argument. Easiest way to do this is to enclose $a in quotes:
$w1.SendKeys("$a");
I want to know how to test if I am currently processing the last line of input in a commandlet (this might not be the correct term) in Powershell.
I have a series of SQL statments in a log file and I want to construct an union of them. Currently I have (on one line, but I show them separated here for readability here)
echo "SELECT * FROM ( " > union.log
cat sql.log | %{$_ + "`n UNION `n"} >> union.log
echo " ) AS qwerty " >> union.log
However, this results in a superfluous UNION in the output. Therefore, I could like to test in the cmdlet (that's what I think the %{...} is called) if the current line is the last one and then not append the string "UNION".
Is this even possible?
PS- This is a simplified example, in my real case I have to do some grepping and substring to extract the queries.
Better to use the -join operator if you want to take a series of strings and merge them together with something between.
Eg.
#('a', 'b', 'c') -join ':'
results in a:b:c.