how to concatenate two variables when one has spaces in it in AzureDevops batch file - batch-file

I have this input in my ps script. I am passing $(Build.ArtifactStagingDirectory) system variable to get the path in the input %1. The path has spaces i.e. E:\Build Agents\Agent2\_work\15\a. I need to concatenate this with other path but I am not able to do so.
set BuildDrop=%1
set Directory=%BuildDrop% + "\adapters\bin"
This is my output, which is incorrect as Directory should be something like E:\Build Agents\Agent2\_work\15\a\adapters\bin. How to solve this?
set BuildDrop="E:\Build Agents\Agent2\_work\15\a"
set Directory="E:\Build Agents\Agent2\_work\15\a" + "\adapters\bin"
My task is like this in my build pipeline
Task : Batch script
Description : Run a Windows command or batch script and optionally allow it to change the environment
Version : 1.1.10

I found the solution for this. Since my %1 had space I needed to remove apostrohphe before assigning. I did it using ~ variable. working code looks like this.
set "BuildDrop=%~1"
set "Directory=%BuildDrop%\adapters\bin"

You could try this :
set BuildDrop=%1
set Directory= %BuildDrop%\adapters\bin
echo %Directory%
Sample Output
This is the concatenation code.
%BuildDrop%\adapters\bin
In my above sample I am trying to concatenate C:\Users\svijay\Desktop & \adapters\bin using the batch script
And the output : C:\Users\svijay\Desktop\adapters\bin
UPDATE :
#echo off
set BuildDrop=%1
set Directory= %BuildDrop%\adapters\bin
set Directory= %Directory:"=%
echo %Directory%
If you have space, you could provide the "" to provide input.
In your code you could remove the double quotes.
%Directory:"=% - Removes the Double quotes from the string literal.
Sample output :

You may use this to create Azure DevOps variable containing your path
powershell: |
$directory = $(Build.ArtifactStagingDirectory)
$newDirectory = $directory + "\adapters\bin"
Write-Host "##vso[task.setvariable variable=testvar]$newDirectory "
and if you need set variable in powershell script
$BuildDrop=$args[0]
$Directory=$BuildDrop + "\adapters\bin"
Here you have an article how to use parameters in powershell.

We can use powershell task in the Azure DevOps to concatenate two variables.
Sample:
Write-Host "Build.ArtifactStagingDirectory is $(Build.ArtifactStagingDirectory)"
Write-Host "Build.ArtifactStagingDirectory is $(Build.ArtifactStagingDirectory)\adapters\bin"
Result:
In addition, I found a similar issue, please also check it.
Update1
Use power shell script in the Azure DevOps pipeline
$testpath1 = "E:\Build Agents\Agent2\_work\15\a"
$testpath2 = "\adapters\bin"
Write-Host "path is $($testpath1)$($testpath2)"
Result:
Use local power shell
Result:
Update2
.bat file
set testpath1=E:\Build Agents\Agent2\_work\15\a
set testpath2=\adapters\bin
set newpath=%testpath1%%testpath2%
echo %newpath%
Pipeline result:

Related

Direct command and using bat file difference?

I can get image size with this code if I run direct cmd:
identify -format "%wx%h" E:\Image.jpg
(the result = 640x480)
However, if I put this code in a bat file,
#echo off
identify -format "%wx%h" E:\Image.jpg
it doesn't work.
so, (the result = h) ??
How can I do this. Thanks...
Switch % to %% in a Batchfile.
Your code should be something like: identify -format "%wx%h" E:\Image.jpg

Combine two variables/paths in bat script

I am trying to combine zip all the contents of a folder. Following is in my bat file:
set DESIGN="%1"
set new_var="%2"
powershell Compress-Archive -Path %DESIGN%\* -DestinationPath %DESIGN%\%new_var%.zip
Below is how I am calling it:
zip_power.bat C:\rehost res_h
Below is the cmd output:
It is adding additional quotes. How do I concatenate two paths?
You are adding the " marks when you are setting the variables... so they are included when you inject them into the later statement.
try
set DESIGN=%1

Running Powershell command in a command line/batch file

I am creating a batch file which involves converting a SID to local/domain username. Since we can not achieve this using the command prompt I am planning to use powershell and I have the PS command as well. I am able to run it in powershell console without any issue, but not sure how to use it in command prompt as a SINGLE LINE(to use it in batch file). I have already tried the below.
Powershell command which works perfectly in PS console -
([System.Security.Principal.SecurityIdentifier]("S-1-5-32-544")).Translate([System.Security.Principal.NTAccount]).Value
Command lines which I have already tried but with no success -
powershell -command ([System.Security.Principal.SecurityIdentifier]("S-1-5-32-544")).Translate([System.Security.Principal.NTAccount]).Value
powershell -command {([System.Security.Principal.SecurityIdentifier]("S-1-5-32-544")).Translate([System.Security.Principal.NTAccount]).Value}
What am I doing wrong? Is it due to any escape characters or am I missing any powershell command parameters? Any help is greatly appreciated.
powershell -command "([System.Security.Principal.SecurityIdentifier]('S-1-5-32-544')).Translate([System.Security.Principal.NTAccount]).Value"
worked for me. Just a question of changing the innermost double-quotes to singles around the SID.
Alternatively, escape them with a backslash
powershell -command "([System.Security.Principal.SecurityIdentifier](\"S-1-5-32-544\")).Translate([System.Security.Principal.NTAccount]).Value"
Here is a short VBScript script that uses WMI to do the conversion for you:
Dim SID
SID = WScript.Arguments.Item(0)
Dim SWbemServices, SWbemObject
Set SWbemServices = GetObject("winmgmts:root/CIMV2")
Set SWbemObject = SWbemServices.Get("Win32_SID.SID='" & SID & "'")
WScript.Echo SWbemObject.ReferencedDomainName & "\" & SWbemObject.AccountName
You would of course need to capture this script's output from your shell script (batch file).
You're actually really close. What you want to do is this:
powershell -command "& {([System.Security.Principal.SecurityIdentifier]('S-1-5-32-544')).Translate([System.Security.Principal.NTAccount]).Value}"
I tested this using the Get-WmiObject cmdlet. In a standard, but elevated cmd shell I entered:
powershell -command "& {get-wmiobject win32_operatingsystem}"
At that point the wmi object data as returned by powershell was written to the console. You can also load the command into a variable like so:
set wmi=powershell -command "& {get-wmiobject win32_operatingsystem}"
If you call the variable %wmi% there's a delay before it prints. The command itself is in the variable so everytime you call the variable it'll execute the powershell code and return the result.
An alternative answer is to use a base64 encodedcommand switch.
#ECHO OFF
powershell -encodedcommand "KABbAFMAeQBzAHQAZQBtAC4AUwBlAGMAdQByAGkAdAB5AC4AUAByAGkAbgBjAGkAcABhAGwALgBTAGUAYwB1AHIAaQB0AHkASQBkAGUAbgB0AGkAZgBpAGUAcgBdACgAIgBTAC0AMQAtADUALQAzADIALQA1ADQANAAiACkAKQAuAFQAcgBhAG4AcwBsAGEAdABlACgAWwBTAHkAcwB0AGUAbQAuAFMAZQBjAHUAcgBpAHQAeQAuAFAAcgBpAG4AYwBpAHAAYQBsAC4ATgBUAEEAYwBjAG8AdQBuAHQAXQApAC4AVgBhAGwAdQBlAA=="
PAUSE
When decoded, you'll see it's the OP's original snippet (with the double quotes preserved). Maybe overkill for the OP, but useful for dev's with larger scripts. Plus my original answer was identical to someone elses, so I had to edit.
powershell.exe -EncodedCommand
Accepts a base-64-encoded string version of a command. Use this parameter
to submit commands to Windows PowerShell that require complex quotation
marks or curly braces.

Launch PL/SQL script from batch file (with arguments)

I'm strugling at passing arguments to a PL/SQL script from a Windows .bat file.
Here is the content of the batch file :
#echo off
set mName = "test"
exit | sqlplus <connection_string> #test.sql %mName%
pause
And here is the content of test.sql :
set verify off
set serveroutput on size unlimited
BEGIN
DBMS_OUTPUT.PUT_LINE('&&1');
END;
/
I would expect to see "test" appearing in the shell but instead, I get the following message :
Enter value for 1:
SP2-0546: User requested Interrupt or EOF detected.
Does anyone have a solution to this problem ?
Remove the spaces around your = sign. Here's my working batch file:
#echo off
set mName="test"
exit | sqlplus <connection_string> #test.sql %mName%
pause
Also note that you can use the -L option on sqlplus for batch jobs:
-L Attempts to log on just once, instead of reprompting on error.

How to convert this sqlcmd script to an Invoke-Sqlcmd script?

I have a SQL script designed to be executed by sqlcmd, and a Command script that executes sqlcmd with the correct parameters.
I want to convert the Command script to a PowerShell script that uses Invoke-Sqlcmd instead of sqlcmd.
The SQL script, the Command script, and the new PowerShell script all live in the directory C:\Users\iain.CORP\SqlcmdQuestion.
SQL Script
The SQL script is called ExampleQuery.sql. It selects a string literal. The value of the string literal is set by sqlcmd at runtime to the value of the ComputerName sqlcmd scripting variable. The code looks like this:
SELECT '$(ComputerName)';
Command Script
The command script is called ExecQuery.cmd. It calls sqlcmd to execute ExampleQuery.sql and sets the value of the scripting variable ComputerName to the value of the environment variable COMPUTERNAME. The code looks like this:
sqlcmd -i ExampleQuery.sql -v ComputerName = %COMPUTERNAME%
When I open a command prompt, the default working directory is C:\Users\iain.CORP. I change the to the directory containing the files, and run the Command script:
cd C:\Users\iain.CORP\SqlcmdQuestion
ExecQuery.cmd
I see this output:
---------
SKYPC0083
(1 rows affected)
The script successfully selects a string literal set by sqlcmd.
PowerShell Script
The PowerShell script is called ExecQuery.ps1. It is supposed to do the same as the command script, using Invoke-Sqlcmd instead of sqlcmd. The code looks like this:
Add-PSSnapin SqlServerCmdletSnapin100
Add-PSSnapin SqlServerProviderSnapin100
Invoke-Sqlcmd -InputFile 'ExampleQuery.sql' -Variable "ComputerName = $Env:COMPUTERNAME"
When I open a PowerShell prompt, the default working directory is Z:\. I change to the directory containing the files, and run the PowerShell script:
cd C:\Users\iain.CORP\SqlcmdQuestion
.\ExecQuery.ps1
I see this output:
Invoke-Sqlcmd : Could not find file 'Z:\ExampleQuery.sql'.
At C:\Users\iain.CORP\SqlcmdQuestion\ExecQuery.ps1:4 char:14
+ Invoke-Sqlcmd <<<< -InputFile 'ExampleQuery.sql' -Variable "ComputerName = $Env:COMPUTERNAME"
+ CategoryInfo : InvalidResult: (:) [Invoke-Sqlcmd], FileNotFoundException
+ FullyQualifiedErrorId : ExecutionFailed,Microsoft.SqlServer.Management.PowerShell.GetScriptCommand
The PowerShell script raises an error because Invoke-Sqlcmd can't find the the input file in the Z:\ directory, which happens to be the default working directory.
The Command script found the script in the current working directory.
How do I make Invoke-Sqlcmd use the current working directory instead of the default working directory?
For this answer, assume that the directory C:\Users\iain.CORP\SqlcmdQuestion exists and that executing dir at that location produces the following output, as implied by the question:
Directory: C:\Users\iain.Corp\SqlcmdQuestion
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 26/09/2012 15:30 27 ExampleQuery.sql
-a--- 26/09/2012 15:30 61 ExecQuery.cmd
-a--- 26/09/2012 15:34 172 ExecQuery.ps1
PowerShell ignores the working directory by design
My question has a false premise:
How do I make Invoke-Sqlcmd use the current working directory instead of the default working directory?
The cmdlet does use the current working directory. The problem is that I didn't change the working directory at all in my PowerShell session.
In PowerShell, cd is an alias for the Set-Location cmdlet. You can prove this using the Get-Alias cmdlet:
Get-Alias cd
Output:
CommandType Name Definition
----------- ---- ----------
Alias cd Set-Location
Alex Angelopoulos explains:
[A]lthough PowerShell's location is analogous to the working directory, the location is not the same thing as the working directory. In fact, PowerShell doesn't touch the working directory.
Set-Location does not set the working directory. It sets the working location, which is a similar but distinct concept in PowerShell.
You can prove this by inspecting the working directory using the .NET property Environment.CurrentDirectory after setting the working location using cd as in the question:
cd C:\Users\iain.CORP\SqlcmdQuestion
Environment::CurrentDirectory
Output:
Z:\
I would guess this design decision was made to be consistent. The working directory would be undefined when, for example, the working location were set to a registry hive.
Invoke-Sqlcmd violates this design principle
Invoke-Sqlcmd violates PowerShell's general design principle to use the working location rather than the working directory. Most cmdlets use the working location to resolve relative paths, but Invoke-Sqlcmd is an exception.
Using the ILSpy disassembler and a little intuition to inspect the containing assembly Microsoft.SqlServer.Management.PSSnapins, I believe I have found the reason for the error.
I believe that the cmdlet's parameter -InputFile is implemented by the method IncludeFileName. ILSpy's disassembly of the method looks like this:
// Microsoft.SqlServer.Management.PowerShell.ExecutionProcessor
public ParserAction IncludeFileName(string fileName, ref IBatchSource pIBatchSource)
{
if (!File.Exists(fileName))
{
ExecutionProcessor.sqlCmdCmdLet.TerminateCmdLet(new FileNotFoundException(PowerShellStrings.CannotFindPath(fileName), fileName), "ExecutionFailureException", ErrorCategory.ParserError);
return ParserAction.Abort;
}
BatchSourceFile batchSourceFile = new BatchSourceFile(fileName);
pIBatchSource = batchSourceFile;
return ParserAction.Continue;
}
Invoke-Sqlcmd uses the .NET method File.Exists to check whether the specified input file exists. The method's documentation remarks that relative paths are resolved using the working directory:
The path parameter is permitted to specify relative or absolute path
information. Relative path information is interpreted as relative to
the current working directory. To obtain the current working
directory, see GetCurrentDirectory.
This suggests that File.Exists would return false in this case, which would cause the error message seen in the question. You can prove this by executing the method directly from the prompt:
cd C:\Users\iain.CORP\SqlcmdQuestion
[IO.File]::Exists('ExecQuery.sql')
Output:
False
The method returns false, so the cmdlet terminates with a 'file not found' error.
You can work around the unusual behavior
There are two workarounds for Invoke-Sqlcmd using the working directory instead of the working location to resolve relative paths:
Always use an absolute path as the value of the -InputFile parameter. CandiedCode's answer shows how to do this.
Set the working directory and use a relative path.
I solved the problem without side-effects by modifying ExecQuery.ps1 like this:
Add-PSSnapin SqlServerCmdletSnapin100
Add-PSSnapin SqlServerProviderSnapin100
$RestoreValue = [Environment]::CurrentDirectory
[Environment]::CurrentDirectory = Get-Location
Invoke-Sqlcmd -InputFile 'ExampleQuery.sql' -Variable "ComputerName = $Env:COMPUTERNAME"
[Environment]::CurrentDirectory = $RestoreValue
I see this output:
Column1
-------
SKYPC0083
Success!
The new script sets the working directory to match the working location before executing Invoke-Sqlcmd. To avoid unintended side-effects of changing the working directory, the scrtipt restores the working directory value before completing.
Setting the current directory is described in this Channel 9 thread. The example there uses the Directory.SetCurrentDirectory method, but I find it simpler to set the property directly.
You could fully qualify the Inputfile location:
Invoke-Sqlcmd -InputFile 'C:\Users\iain.CORP\SqlcmdQuestion\ExampleQuery.sql' -Variable "ComputerName = $Env:COMPUTERNAME"
And use a variable to drive the script location:
$FileLocation = 'C:\Users\iain.CORP\SqlcmdQuestion\'

Resources