7

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?

0

3 Answers 3

14

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.
Sign up to request clarification or add additional context in comments.

4 Comments

Any advice for the situation where the variable can contain either a $ or a ' character? We used the single quote to get around the $-problem, but now we're facing a similar issue with the single quote character.
@Robba, I realize I never responded: For values with embedded ' chars., you can use "..." quoting (assuming it doesn't also contain " chars.), though if the value also contains $, this could lead to unwanted interpolation on the PowerShell side. If neither works for you, try accessing the variable as an - unquoted - environment variable in PowerShell, which bypasses quoting problems (I cannot personally verify this, but supposedly all system and user Azure variables are also defined as environment variables, in all-uppercase, with any . chars. in the name replaced with _)
thanks for the tip. I never thought of accessing the value as an environment variable. In this case I just went with changing the password, though I do wonder if passwords/secrets are also passed in as environment variable, or if this is only done for regular variables.
@Robba, the docs say that secret variables aren't exported as environment variables by default (for security reasons), but you can explicitly map them onto environment variables.
2

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)"

2 Comments

That worked, but could you explain why this works? I come from OOP languages and this doesn't make any sense.
In PowerShell, you must add quotes for strings, without it PowerShell thinks it's a command.
0

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'

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.