Saw these variables in the debugger and I'm not sure what they do.
$_ The current pipeline object; used in script blocks, filters, the process clause of functions, where-object, foreach-object and switch
$^ contains the first token of the last line input into the shell
$$ contains the last token of last line input into the shell
$? Contains the success/fail status of the last statement
Source: http://www.computerperformance.co.uk/powershell/powershell_variables.htm#%24Dollar_variables_
Read
Get-Help About_Automatic_Variables
Its all about the goodness of automatic variables that get created when using PowerShell console.
In PowerShell, a dollar sign preceding a name indicates a variable. The symbols in question are just special cases of variables provided by the PowerShell environment. They are also known as "automatic" variables. More specifically:
$$ is a variable containing the last token of the last line input into the shell(does not contain the whole command)
$^ is a variable containing the first token of the last line input into the shell(does not contain the whole command)
$? is a variable containing the success or failure of the last statement(False if the previous command ended with an error; True otherwise.)
Related
I am writing a program prog.exe that retrieves all arguments that are passed to it (in the form of a "sentence", not standalone arguments).
I just realized that in some cases only part of the line is retrieved, and this is when there are #parameters:
PS > ./prog.exe this is a #nice sentence
Only this, is and a are retrieved. In case I do not use # I get all of them. I presume this is because everything after the # is interpreted by Powershell as a comment.
Is there a way to retrieve everything that is on the command line?
If this makes a difference, I code in Go and get the arguments via os.Args[1:].
You can prevent PowerShell from interpreting # as a comment token by explicitly quoting the input arguments:
./prog.exe one two three '#four' five
A better way exists, though, especially if you don't control the input: split the arguments into individual strings then use the splatting operator # on the array containing them:
$paramArgs = -split 'one two three #four five'
./prog.exe #paramArgs
Finally, using the --% end-of-parsing token in a command context will cause the subsequent arguments on the same line to be passed as-is, no parsing of language syntax:
./prog.exe --% one two three #four five
I'm trying to use sqlplus to do an Oracle query for the first time in a PowerShell script. I get this error message:
At line:1 char:73
+ ... user/pw#RRRPRD.company.net:1521/RRRPRDC #"C:\Users\ ...
+ ~
No characters are allowed after a here-string header but before the end of the line.
It seems to be pointing to the C: after #". Any ideas? I seem to be doing what is at this example. I get the same error when I try to do echoargs of the connection info.
This is my powershell code I am testing at the command line since it hangs forever running the program:
sqlplus user/pw#RRRPRD.company.net:1521/RRRPRDC #"C:\Users\me\Documents\2021\temp endToEnd\short.sql"
This is using powershell 5.1. Any ideas? I see here string header, but since I am following the example that was accepted in the link for sqlplus above, it's unclear to me what's wrong with it.
Replace
#"C:\Users\me\Documents\2021\temp endToEnd\short.sql"
with any of the following:
`#"C:\Users\me\Documents\2021\temp endToEnd\short.sql"
"#C:\Users\me\Documents\2021\temp endToEnd\short.sql"
'#C:\Users\me\Documents\2021\temp endToEnd\short.sql'
Note: Using a verbatim (single-quoted) string ('...') is arguably the best choice here, given that the path contains no variable references; if it did, a expandable (double-quoted) string ("...") would be equired.
All variations end up passing the following string verbatim to sqlplus, which I presume is your intent:
#C:\Users\me\Documents\2021\temp endToEnd\short.sql
Presumably, you're trying to pass # as a verbatim part of an argument to sqlplus - a common convention among CLIs is to use #<file-path> to request that argument data be taken from a file rather than using the argument value itself.
However, unlike in other shells, # is a metacharacter in PowerShell that serves a variety of purposes.
Thus, a # that should be a verbatim character at the start of an argument must either be escaped (with `) or part of a quoted string, as shown above. See the conceptual about_Special_Characters help topic.
If an unescaped argument-initial # is followed by " or ', PowerShell thinks you're trying to create a here-string, which has special, multi-line syntax requirements; the error message indicates that they're not met.
I recently learned that you can use special characters in PowerShell variables, e.g.: ${hello world!}, but then I stumbled across this:
${^}
What does it do? I first thought it referenced the newest created variable, but it does other stuff I haven't really figured out.
The documentation says:
Contains the first token in the last line received by the session.
Examples:
dir c:\windows 🡒 dir
0..9 🡒 0
&{ dir } 🡒 &
It was likely introduced to get the last command used (dir, copy, rm, ...), but in reality that will only work for the most simplest cases, and thus it's not very useful.
The corresponding $$ returns the last token from the last line.
Note: The curly braces {} are only necessary for variable names containing characters which are not allowed in variables, except automatic variables (look here). In this case, you can omit them:
$^
It shows the first word/token in the last executed command.
I downloaded the npm package for merge junit reports - https://www.npmjs.com/package/junit-merge.
The problem is that I have multiple files to merge and I am trying to use string variable to hold file names to merge.
When I write the script myslef like:
junit-merge a.xml b.xml c.xml
This works, the merged file is being created, but when I do it like
$command = "a.xml b.xml c.xml"
junit-merge $command
This does not work. The error is
Error: File not found
Has anyone faced similar issues?
# WRONG
$command = "a.xml b.xml c.xml"; junit-merge $command
results in command line junit-merge "a.xml b.xml c.xml"[1], i.e. it passes a string with verbatim value a.xml b.xml c.xml as a single argument to junit-merge, which is not the intent.
PowerShell does not act like POSIX-like shells such as bash do in this regard: In bash, the value of variable $command - due to being referenced unquoted - would be subject to word splitting (one of the so-called shell expansions) and would indeed result in 3 distinct arguments (though even there an array-based invocation would be preferable).
PowerShell supports no bash-like shell expansions[2]; it has different, generally more flexible constructs, such as the splatting technique discussed below.
Instead, define your arguments as individual elements of an array, as justnotme advises:
# Define the *array* of *individual* arguments.
$command = "a.xml", "b.xml", "c.xml"
# Pass the array to junit-merge, which causes PowerShell
# to pass its elements as *individual arguments*; it is the equivalent of:
# junit-merge a.xml b.xml c.xml
junit-merge $command
This is an application of a PowerShell technique called splatting, where you specify arguments to pass to a command via a variable:
Either (typically only used for external programs, as in your case):
As an array of arguments to pass individually as positional arguments, as shown above.
Or (more typically when calling PowerShell commands):
As a hashtable to pass named parameter values, in which you must replace the $ sigil in the variable reference with #; e.g., in your case #command; e.g., the following is the equivalent of calling Get-ChildItem C:\ -Directory:
$paramVals = #{ LiteralPath = 'C:\'; Directory = $true }; Get-ChildItem #paramVals
Caveat re array-based splatting:
Due to a bug detailed in GitHub issue #6280, PowerShell doesn't pass empty arguments through to external programs (applies to all Windows PowerShell versions / and as of PowerShell (Core) 7.2.x; a fix may be coming in 7.3, via the $PSNativeCommandArgumentPassing preference variable, which in 7.2.x relies on an explicitly activated experimental feature).
E.g., foo.exe "" unexpectedly results in just foo.exe being called.
This problem equally affects array-based splatting, so that
$cmdArgs = "", "other"; foo.exe $cmdArgs results in foo.exe other rather than the expected foo.exe "" other.
Optional use of # with array-based splatting:
You can use the # sigil also with arrays, so this would work too:
junit-merge #command
There is a subtle distinction, however.
While it will rarely matter in practice,
the safer choice is to use $, because it guards against (the however hypothetical) accidental misinterpretation of a --% array element you intend to be a literal.
Only the # syntax recognizes an array element --% as the special stop-parsing symbol, --%
Said symbol tells PowerShell not to parse the remaining arguments as it normally would and instead pass them through as-is - unexpanded, except for expanding cmd.exe-style variable references such as %USERNAME%.
This is normally only useful when not using splatting, typically in the context of being able to use command lines that were written for cmd.exe from PowerShell as-is, without having to account for PowerShell's syntactical differences.
In the context of splatting, however, the behavior resulting from --% is non-obvious and best avoided:
As in direct argument passing, the --% is removed from the resulting command line.
Argument boundaries are lost, so that a single array element foo bar, which normally gets placed as "foo bar" on the command line, is placed as foo bar, i.e. effectively as 2 arguments.
[1] Your call implies the intent to pass the value of variable $command as a single argument, so when PowerShell builds the command line behind the scenes, it double-quotes the verbatim a.xml b.xml c.xml string contained in $command to ensure that. Note that these double quotes are unrelated to how you originally assigned a value to $command.
Unfortunately, this automatic quoting is broken for values with embedded " chars. - see this answer, for instance.
[2] As a nod to POSIX-like shells, PowerShell does perform one kind of shell expansion, but (a) only on Unix-like platforms (macOS, Linux) and (b) only when calling external programs: Unquoted wildcard patterns such as *.txt are indeed expanded to their matching filenames when you call an external program (e.g., /bin/echo *.txt), which is feature that PowerShell calls native globbing.
I had a similar problem. This technique from powershell worked for me:
Invoke-Expression "junit-merge $command"
I also tried the following (from a powershell script) and it works:
cmd / c "junit-merge $command"
This is a weird one. Normally when I execute an external command from powershell I use the & operator like this:
& somecommand.exe -p somearguments
However, today I came across the . operator used like this:
.$env:systemdrive\chocolatey\chocolateyinstall\chocolatey.cmd install notepadplusplus
What purpose does the period serve in this scenario? I don't get it.
The "." dot sourcing operator will send AND receive variables from other scripts you have called. The "&" call operator will ONLY send variables.
For instance, considering the following:
Script 1 (call-operator.ps1):
clear
$funny = "laughing"
$scriptpath = split-path -parent $MyInvocation.MyCommand.Definition
$filename = "laughing.ps1"
"Example 1:" # Call another script. Variables are passed only forward.
& $scriptpath\$filename
"Example 2:" # Call another script. Variables are passed backwards and forwards.
. $scriptpath\$filename
$variableDefinedInOtherScript
Script 2 (laughing.ps1):
# This is to test the passing of variables from call-operator.ps1
"I am $funny so hard. Passing variables is so hilarious."
$variableDefinedInOtherScript = "Hello World!"
Create both scripts and ONLY run the first one. You'll see that the "." dot sourcing operator sends and receives variables.
Both have their uses, so be creative. For instance, the "&" call operator would be useful if you wanted to modify the value(s) of variables in another script while preserving the original value(s) in the your current script. Kinda a safeguard. ;)
The Short:
It is a Special Operator used to achieve what regular operators cannot achieve. This particular operator . actually has two distinctively different Special Operator use cases.
The Long:
As with any other language, scripting or otherwise, PowerShell script also supports many different types of Operators to help manipulate values. These regular operators include:
Arithmetic
Assignment
Comparison
Logical
Redirection
List item
Split and Join
Type
Unary
However, PowerShell also supports whats known as Special Operators which are used to perform tasks that cannot be performed by the other types of operators.
These Special Operators Include:
#() Array subexpression operator
& Call operator
[ ] Cast operator
, Comma operator
. Dot sourcing operator
-f Format operator
[ ] Index operator
| Pipeline operator
. Property dereference operator
.. Range operator
:: Static member operator
$( ) Subexpression operator
. Dot sourcing operator: is used in this context to allow a script to run in the current scope essentially allowing any functions, aliases, and variables which has been created by the script to be added to the current script.
Example:
. c:\scripts.sample.ps1
NoteThat this application of the . Special Operator is followed by a space to distinguish it from the (.) symbol that represents the current directory
Example:
. .\sample.ps1
. Property dereference operator: Allows access to the properties and methods of of an object which follows the . by indicating that the expression on the left side of the . character is an object and the expression on the right side of the is an object member (a property or method).
Example:
$myProcess.peakWorkingSet
(get-process PowerShell).kill()
Disclaimer & Sources:
I had the same question while looking at a PowerShell script that I was trying to expand on its feature sets and landed here when doing my research for the answer. However I managed to find my answer using this magnificent write up on the Microsoft Development Network supplemented with this further expansion of the same ideas from IT Pro.
Cheers.
The dot is a call operator:
$a = "Get-ChildItem"
. $a # (executes Get-ChildItem in the current scope)
In your case, however, I don't see what it does.
.Period or .full stop for an objects properties; like
$CompSys.TotalPhysicalMemory
See here: http://www.computerperformance.co.uk/powershell/powershell_syntax.htm#Operators_
This answer is to expand slightly upon those already provided by David Brabant and his commenters. While those remarks are all true and pertinent, there is something that has been missed.
The OPs use of & when invoking external commands is unnecessary. Omitting the & would have no effect (on the example of his usage). The purpose of & is to allow the invocation of commands whose names are the values of a (string) expression. By using the & above, powershell then (essentially) treats the subsequent arguments as strings, the first of which is the command name that & duly invokes. If the & were omitted, powershell would take the first item on the line as the command to execute.
However, the . in the second example is necessary (although, as noted by others, & would work just as well in this case). Without it, the command line would begin with a variable access ($env:systemdrive) and so powershell would be expecting an expression of some form. However, immediately following the variable reference is a bare file path which is not a valid expression and will generate an error. By using the . (or &) at the beginning of the line, it is now treated as a command (because the beginning doesn't look like a valid expression) and the arguments are processed as expandable strings (" "). Thus, the command line is treated as
. "$env:systemdrive\chocolatey\chocolateyinstall\chocolatey.cmd" "install" "notepadplusplus"
The first argument has $env:systemdrive substituted into it and then . invokes the program thus named.
Note: the full description of how powershell processes command line arguments is way more complicated than that given here. This version was cut down to just the essential bits needed to answer the question. Take a look at about_Parsing for a comprehensive description. It is not complete but should cover most normal usage. There are other posts on stackoverflow and github (where powershell now resides) that cover some of the seemingly quirky behaviour not listed in the official documentation. Another useful resource is about_Operators though again this isn't quite complete. An example being the equivalence of . and & when invoking something other than a powershell script/cmdlet getting no mention at all.