Pipeline Array Variable (Classic) Causing powershell exception [duplicate] - powershell

I try to concatenate string to construct a path:
$SourceDirectoryPath = $(System.DefaultWorkingDirectory) + "/solution/project/bin/Debug"
$TargetFilePath = $(System.DefaultWorkingDirectory) + "/solution/project/bin/Debug/" + $(Release.ReleaseName) +$(Release.EnvironmentName)
but instead of getting string concatenated I get error for the second line:
d:\a\r1\a : The term 'd:\a\r1\a' is not recognized as the name of a
cmdlet, function, script file, or operable program. Check the
spelling of the name, or if a path was included, verify that the path
is correct and try again. At
D:\a_temp\9de874c9-3acd-4a19-a4dd-763074d38e40.ps1:2 char:25
where obviously d:\a\r1\a is a $(System.DefaultWorkingDirectory) but why it throws this error instead of just concatenating the string?

tl;dr
It is Azure that expands $(System.DefaultWorkingDirectory) before PowerShell sees the resulting commands; if the expanded $(...) value is to be seen as a string by PowerShell, it must be enclosed in quotes ('$(...)'):
Using $(...) (Azure macro syntax) embeds the Azure variable's verbatim value in the command text that PowerShell ends up interpreting.
Note: Azure's macro syntax - which is evaluated before PowerShell sees the resulting command text - is not to be confused with PowerShell's own subexpression operator, $(...).
For string values this means that you situationally have to surround the macro with quotes in order to make it work syntactically in PowerShell code, for which '...'-quoting (single-quoting) is best: '$(System.DefaultWorkingDirectory)'
Shayki Abramczyk's answer provides an effective solution, but let me provide some background information:
The variable expansion (substitution) that Azure performs via macro syntax ($(...)) functions like a preprocessor: it replaces the referenced variable with its verbatim value.
You need to make sure that this verbatim value works syntactically in the context of the target command.
As currently written:
$SourceDirectoryPath = $(System.DefaultWorkingDirectory) + "/solution/project/bin/Debug"
turns into the following command seen by PowerShell, assuming that the value of Azure property System.DefaultWorkingDirectory is d:\a\r1\a:
$SourceDirectoryPath = d:\a\r1\a + "/solution/project/bin/Debug"
This is a broken PowerShell command, because d:\a\r1\a - due to lack of quoting - is interpreted as a command name or path; that is, an attempt is made to execute putative executable d:\a\r1\a - see about_Parsing.
Therefore, in order for PowerShell to recognize the Azure-expanded value d:\a\r1\a as a string, you need to quote it - see about_Quoting_Rules.
Since the expanded-by-Azure value needs no further interpolation, single quotes are the best choice (for both operands, actually):
$SourceDirectoryPath = '$(System.DefaultWorkingDirectory)' + '/solution/project/bin/Debug'
In fact, you don't need string concatenation (+) at all in your case:
$SourceDirectoryPath = '$(System.DefaultWorkingDirectory)/solution/project/bin/Debug'
You could even combine that with expandable PowerShell strings ("..."), as long as the Azure-expanded value doesn't contain $-prefixed tokens that PowerShell could end up interpreting (unless that is your (unusual) intent).
One caveat re something like "$(System.DefaultWorkingDirectory)/$projectRoot/bin/Debug" (mixing an Azure-expanded value with a PowerShell variable reference) is that Azure's macro syntax ($(...)) looks the same as PowerShell's own subexpression operator, which is typically - but not exclusively - used in order to embed expressions in expandable strings (e.g., in pure PowerShell code, "1 + 1 equals $(1 + 1)").
As of this writing, the Define variables Azure help topic doesn't spell it out, but based on the official comment in a GitHub docs issue, ambiguity is avoided as follows:
There is no escape mechanism; instead, $(...) constructs that do not refer to Azure variables are left unchanged and therefore passed through to PowerShell.
In the typical case, PowerShell expressions will not look like an Azure variable reference (e.g, $($foo.bar) rather than $(foo.bar)), though hypothetically there can be ambiguity: $(hostname), which is a valid PowerShell subexpression, could be preempted by Azure if a hostname Azure variable were defined.
In such a corner case, the solution is to avoid use of an inline script and instead place the code in an external script file.

You need to add quotes " " in the variables:
$SourceDirectoryPath = "$(System.DefaultWorkingDirectory)" + "/solution/project/bin/Debug"
$TargetFilePath = "$(System.DefaultWorkingDirectory)" + "/solution/project/bin/Debug/" + "$(Release.ReleaseName)" + "$(Release.EnvironmentName)"

This should work as well. $( ) outside of doublequotes would only be used to combine two or more statements. Most people don't even know about it.
This is actually incorrect. I didn't know Azure Pipeline syntax. It just shows how confusing combining both Powershell and Azure Pipeline can be. This would work if $System were a Powershell object, not an Azure macro.
$SourceDirectoryPath = $System.DefaultWorkingDirectory + '/solution/project/bin/Debug'

Related

sqlplus - No characters are allowed after a here-string header but before the end of the line

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.

PowerShell - Merge two variables into one

I'm learning PowerShell so please forgive (what I'm sure is) a simple question.
I'm used to coding BATCH scripts and if I wanted to merge %USERDOMAIN% and %USERNAME% I would:
set zFullUsername=%USERDOMAIN%\%USERNAME%
echo %zFullUsername%
How can I do the same in PowerShell?
Thank you for your time.
On a supported Operating System, I wouldn't even bother with environment variables for this:
$zFullUsername = whoami
Then just access it as required:
$zFullUsername
In PowerShell, you can access environment variables in a few different ways. The way I recommend is to use the $env:VAR variable to access them.
$user = $env:USERNAME
$domain = $env:USERDOMAIN
echo "$domain\$user"
Note: \ is not an escape character in the PowerShell parser, ` is.
Similarly to rendering the echo command (echo is an alias of Write-Output btw) you can create a username variable like so:
$fullUserName = "$domain\$user"
Or you can skip right to creating $fullUserName straight from the environment variables:
$fullUserName = "${env:USERDOMAIN}\${env:USERNAME}"
Note: When variables have non-alphanumeric characters in them, the ${} sequence tells PowerShell everything between the ${} is part of the variable name to expand.
It seems the : in $env:VAR is actually an exception to this rule, as"Username: $env:USERNAME" does render correctly. So the ${} sequence above is optional.
To avoid confusion when trying to apply this answer in other areas, if you needed to insert the value of an object property or some other expression within a string itself, you would use a sub-expression within the string instead, which is the $() sequence:
$someVar = "Name: $($someObject.Name)"
When using either ${} or $(), whitespace is not allowed to pad the outer {} or ().

How do I deal with space bar when escaping double quote character in Power-Shell

I am able to escape double quote character in PowerShell however when this includes strings with space escaping does not work correctly.
For example:
I can translate the following ADUC to powershell
Name in ADUC: cn="Jim"
In powershell: "cn=\`"Jim\`""
How do I handle/escape the space between Jim and Carter as below ?
Name in ADUC: cn="Jim Carter"
In powershell: "cn=\`"Jim Carter\`""
The output I get is Jim, the space and string Carter are lost.
PowerShell has hidden re-quoting logic when arguments are passed to external utilities.
What you're seeing is bug in Windows PowerShell (that has since been fixed in PowerShell Core):
In short, "cn=\`"Jim Carter\`"" is passed to an external executable as cn=\"Jim Beam\", which unexpectedly turns into two arguments - note how the argument is not double-quoted as a whole, even though it needs to be.
Workarounds:
As part of a distinguished name in Active Directory, component values (such as Jim Beam for field cn) usually do not need double-quoting, even if they contain spaces. Therefore, perhaps the following will work (depending on the behavior of the executable you're invoking).
someExternalTool.exe "cn=Jim Beam"
If you do need the executable to ultimately see string literal cn="Jim Beam", you must use --%, the stop-parsing symbol:
someExternalTool.exe --% "cn=\"Jim Beam\""
If you want the executable to see cn=\"Jim Beam\", i.e., with the " chars. \-escaped, use someExternalTool.exe --% "cn=\\\"Jim Beam\\\""
Note that use of --% then precludes the use of PowerShell variables as part of the same command.
In PowerShell Core, your original argument, "cn=\`"Jim Carter\`"" is passed as "cn=\"Jim Carter\"" - i.e. with the necessary double-quoting as a whole - so the problem with the unexpected argument splitting wouldn't arise, and your target program would see cn="Jim Beam" after performing its own argument parsing.
If you need the target program to see cn=\"Jim Beam\", you'd have to use "cn=\\\`"Jim Beam\\\`"" - or, using single-quoting, 'cn=\\\"Jim Beam\\\"'
As an aside: The need to \-escape " chars. to pass to external programs - in addition to PowerShell's own escaping - should itself be considered a bug, but it's a longstanding one that probably won't be fixed so as to maintain backward compatibility: see this GitHub issue.

Unquoted tokens in argument mode involving variable references and subexpressions: why are they sometimes split into multiple arguments?

Note: A summary of this question has since been posted at the PowerShell GitHub repository, since superseded by this more comprehensive issue.
Arguments passed to a command in PowerShell are parsed in argument mode (as opposed to expression mode - see Get-Help about_Parsing).
Conveniently, (double-)quoting arguments that do not contain whitespace or metacharacters is usually optional, even when these arguments involve variable references (e.g. $HOME\sub) or subexpressions (e.g., version=$($PsVersionTable.PsVersion).
For the most part, such unquoted arguments are treated as if they were double-quoted strings, and the usual string-interpolation rules apply (except that metacharacters such as , need escaping).
I've tried to summarize the parsing rules for unquoted tokens in argument mode in this answer, but there are curious edge cases:
Specifically (as of Windows PowerShell v5.1), why is the unquoted argument token in each of the following commands NOT recognized as a single, expandable string, and results in 2 arguments getting passed (with the variable reference / subexpression retaining its type)?
$(...) at the start of a token:
Write-Output $(Get-Date)/today # -> 2 arguments: [datetime] obj. and string '/today'
Note that the following work as expected:
Write-Output $HOME/sub - simple var. reference at the start
Write-Output today/$(Get-Date) - subexpression not at the start
.$ at the start of a token:
Write-Output .$HOME # -> 2 arguments: string '.' and value of $HOME
Note that the following work as expected:
Write-Output /$HOME - different initial char. preceding $
Write-Output .-$HOME - initial . not directly followed by $
Write-Output a.$HOME - . is not the initial char.
As an aside: As of PowerShell Core v6.0.0-alpha.15, a = following a simple var. reference at the start of a token also seems to break the token into 2 arguments, which does not happen in Windows PowerShell v5.1; e.g., Write-Output $HOME=dir.
Note:
I'm primarily looking for a design rationale for the described behavior, or, as the case may be, confirmation that it is a bug. If it's not a bug, I want something to help me conceptualize the behavior, so I can remember it and avoid its pitfalls.
All these edge cases can be avoided with explicit double-quoting, which, given the non-obvious behavior above, may be the safest choice to use routinely.
Optional reading: The state of the documentation and design musings
As of this writing, the v5.1 Get-Help about_Parsing page:
incompletely describes the rules
uses terms that aren't neither defined in the topic nor generally in common use in the world of PowerShell ("expandable string", "value expression" - though one can guess their meaning)
From the linked page (emphasis added):
In argument mode, each value is treated as an expandable string unless it begins with one of the following special characters: dollar sign ($), at sign (#), single quotation mark ('), double quotation mark ("), or an opening parenthesis (().
If preceded by one of these characters, the value is treated as a value expression.
As an aside: A token that starts with " is, of course, by definition, also an expandable string (interpolating string).
Curiously, the conceptual help topic about quoting, Get-Help about_Quoting_Rules, manages to avoid both the terms "expand" and "interpolate".
Note how the passage does not state what happens when (non-meta)characters directly follow a token that starts with these special characters, notably $.
However, the page contains an example that shows that a token that starts with a variable reference is interpreted as an expandable string too:
With $a containing 4, Write-Output $a/H evaluates to (single string argument) 4/H.
Note that the passage does imply that variable references / subexpressions in the interior of an unquoted token (that doesn't start with a special char.) are expanded as if inside a double-quoted string ("treated as an expandable string").
If these work:
$a = 4
Write-Output $a/H # -> '4/H'
Write-Output H/$a # -> 'H/4'
Write-Output H/$(2 + 2) # -> 'H/4'
why shouldn't Write-Output $(2 + 2)/H expand to '4/H' too (instead of being treated as 2 arguments?
Why is a subexpression at the start treated differently than a variable reference?
Such subtle distinctions are hard to remember, especially in the absence of a justification.
A rule that would make more sense to me is to unconditionally treat a token that starts with $ and has additional characters following the variable reference / subexpression as an expandable string as well.
(By contrast, it makes sense for a standalone variable reference / subexpression to retain its type, as it does now.)
Note that the case of a token that starts with .$ getting split into 2 arguments is not covered in the help topic at all.
Even more optional reading: following a token that starts with one of the other special characters with additional characters.
Among the other special token-starting characters, the following unconditionally treat any characters that follow the end of the construct as a separate argument (which makes sense):
( ' "
Write-Output (2 + 2)/H # -> 2 arguments: 4 and '/H'
Write-Output "2 + $a"/H # -> 2 arguments: '2 + 4' and '/H', assuming $a equals 4
Write-Output '2 + 2'/H # -> 2 arguments: '2 + 2' and '/H'
As an aside: This shows that bash-style string concatenation - placing any mix of quoted and unquoted tokens right next to each other - is not generally supported in PowerShell; it only works if the 1st substring / variable reference happens to be unquoted. E.g., Write-Output H/'2 + 2', unlike the substrings-reversed example above, produces only a single argument.
The exception is #: while # does have special meaning (see Get-Help about_Splatting) when followed by just a syntactically valid variable name (e.g., #parms), anything else causes the token to be treated as an expandable string again:
Write-Output #parms # splatting (results in no arguments if $parms is undefined)
Write-Output #parms$a # *expandable string*: '#parms4', if $a equals 4
I think what you're sort of hitting here is more the the type "hinting" than anything else.
You're using Write-Output which specifies in it's Synopsis that it
Sends the specified objects to the next command in the pipeline.
This command is designed to take in an array. When it hits the first item as a string like today/ it treats it like a string. When the first item ends up being the result of a function call, that may or may not be a string, so it starts up an array.
It's telling that if you run the same command to Write-Host (which is designed to take in a string to output) it works as you'd expect it to:
Write-Host $(Get-Date)/today
Outputs
7/25/2018 1:30:43 PM /today
So I think you're edge cases you're running up against are less about the parsing, and mor about the typing that powershell uses (and tries to hide).

What does the period '.' operator do in powershell?

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.