How to pass path with spaces to script - powershell

I am trying to use PowerShell to call an EXE that is at a location/path containing spaces. When I call the script from the command line, the EXE's full path is not being passed to the script. Any ideas as to why this is happening?
PowerShell Script Contents (Untitled1.ps1)
Here is the entire script that gets called from the command line:
param(
[string] $ParamExePath
)
function Run-CallThisExe {
param(
[string] $ThisExePath
)
Write-Host "ThisExePath: " "$ThisExePath"
start-process -FilePath $ThisExePath
}
write-host "ParamExePath: " $ParamExePath
Run-CallThisExe -ThisExePath "$ParamExePath"
Command Line String
Here is the command line string being run from the PowerShell script's parent folder:
powershell -command .\Untitled1.ps1 -NonInteractive -ParamExePath "C:\path with spaces\myapp.exe"
Output
Here is what is output after running the script
ParamExePath: C:\path
ThisExePath: C:\path
start-process : This command cannot be run due to the error: The system cannot
find the file specified.
At C:\sample\Untitled1.ps1:11 char:5
+ start-process -FilePath $ThisExePath
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Start-Process], InvalidOperationException
+ FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.Commands.StartProcessCommand

Just change this:
powershell -command .\Untitled1.ps1 -NonInteractive -ParamExePath "C:\path with spaces\myapp.exe"
To This:
powershell -file .\Untitled1.ps1 -NonInteractive -ParamExePath "C:\path with spaces\myapp.exe"
The -Command Parameter used to execute commands for example {Get-Date}
The -File Parameter used to Run .ps1 Script File
-Command
Executes the specified commands (and any parameters) as though they were
typed at the Windows PowerShell command prompt, and then exits, unless
NoExit is specified. The value of Command can be "-", a string. or a
script block.
-File
Runs the specified script in the local scope ("dot-sourced"), so that the
functions and variables that the script creates are available in the
current session. Enter the script file path and any parameters.
File must be the last parameter in the command, because all characters
typed after the File parameter name are interpreted
as the script file path followed by the script parameters.
Type Powershell /? to get full details on each Parameter

A couple of other ways to call it:
powershell -command .\Untitled1.ps1 -NonInteractive "-ParamExePath 'C:\path with spaces\myapp.exe'"
powershell -command ".\Untitled1.ps1 -ParamExePath 'C:\path with spaces\myapp.exe'" -NonInteractive

Note that if you pass a folder path to Powershell that has a trailing backslash it cannot handle it. e.g. -ParamFolder "C:\project folder\app\bin debug\". The parameter string ends up with a double quote at the end. So when you try to append the name of a file to it you end up with something like C:\project folder\app\bin debug"Filename.txt. In this case you have to send in a second backslash at the end.

Related

Powershell - How to pass arguments with apostrophes in them?

I'm using a third party tool to do some AD manipulation. I run a powershell script and pass arguments to it.
Everything works except if that argument contains an apostrophe in it like Jerry O'Connor. I've tried lots of different escape combinations without any luck.
Here is my command: script.ps1 -name "'%name%'" and name contain is Jerry O'Connor.
The error is
Result:The string is missing the terminator: '.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : TerminatorExpectedAtEndOfString
I've tried:
script.ps1 -name "'%name%'"
script.ps1 -name \"%name%\"
script.ps1 -name ''name''
all with same error.
If you run this at the PS CMD level you'll see the error
powershell echo -name "'Jerry O'Connor'"
Anyone know how to pass an argument to script.ps1 -name "name" where that argument contains an apostrophe?
Cheers
You need to escape any ' chars. inside the value of %name% as '' to make them work inside a single-quoted PowerShell string, which from cmd.exe you can do with %name:'=''%:
From cmd.exe / a batch file:
powershell.exe -Command ./script.ps1 -name "'%name:'=''%'"
If %name% contains verbatim Jerry O'Connor, the above expands to the following, which should work as intended:
powershell.exe -Command ./script.ps1 -name "'Jerry O''Connor'"
However, you can simplify quoting if you use the -File CLI parameter instead of -Command:
powershell.exe -File ./script.ps1 -name "%name%"
See also:
For guidance on when to use -Command vs. -File, see this answer
For a comprehensive overview of the PowerShell CLI, see this answer
Try the escape character as shown here:
$Test = "`'test`'"
$Test

Powershell Removing Quotes Argument

I'm using Start-Process to start another instance of Powershell as an administrator but when I try to pass the argument list, whether as a variable or as a plain string, Powershell removes the quotes. Below is the command I'm using:
$argu = '-noexit "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat"';
powershell Start-Process -Verb RunAs -FilePath powershell -ArgumentList $argu
This is the error I get:
x86 : The term 'x86' 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 line:1 char:88
+ ... Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\v ...
+ ~~~
+ CategoryInfo : ObjectNotFound: (x86:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Thank you in advance for any help.
Update:
$argu = '''-noexit ""C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat""''';
powershell Start-Process -Verb RunAs -FilePath powershell -ArgumentList $argu
This almost fixes it but now I'm getting the error above in the second window instead of the first.
(A) From inside PowerShell:
$argu = '-noexit -command & \"C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat\"'
Start-Process -Verb RunAs -FilePath powershell -ArgumentList $argu
Note: I'm not calling Start-Process via powershell.exe, as there is generally no need for that.
The embedded " are \-escaped, which is what PowerShell requires when you call its CLI (perhaps surprisingly, given that PowerShell-internally it is ` that acts as the escape character).
That said given that the " are embedded inside '...' here, they shouldn't require extra escaping - see below.
The file path to execute is prefixed with call operator &, because you need it in order to execute files that are specified in quoted form.
Note that I've added -Command, which is not strictly necessary in Windows PowerShell, but would be if you ran your command from PowerShell Core (which now defaults to -File).
Alternatively, you could also specify your arguments individually, as part of an array, which is arguably the cleaner solution:
$argu = '-noexit', '-command', '&', 'de',
'\"C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat\"'
Start-Process -Verb RunAs -FilePath powershell -ArgumentList $argu
Sadly, even in this case you need the extra, embedded quoting for arguments that contain spaces, which is a known Start-Process problem being tracked on GitHub.
PowerShell's handling of quoting when calling external programs is generally problematic; the current issues are summarized in this GitHub issue.
(B) From outside PowerShell (cmd.exe, a custom File Explorer context menu):
powershell -command Start-Process -Verb RunAs -FilePath powershell -ArgumentList '-noexit -command . ''C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat'''
single-quoting is now employed (with nested single quotes escaped as ''), because double-quoting would substantially complicate the escaping.
. is used instead of & to execute the .bat file, which avoids a problem with how the & is parsed; while . generally serves a different purpose than &, the two operators behave the same when calling external programs.
If you also want to set the working directory for the PowerShell session that ultimately opens elevated, you need to incorporate an explicit Set-Location (cd) call into the command string, because Start-Process -Verb RunAs always defaults to the SYSTEM32 folder (even the -WorkingDirectory parameter doesn't help in that case).
For that to work safely, however, you must quote the directory path using double-quoting, given that file names may contain single quotes; with %V as the directory path (which File Explorer supplies to commands invoked via custom context menus), the properly escaped Set-Location call looks like this (I wish I were kidding):
Set-Location \"\"\"%V%\"\"\"
Integrated into the full command (using Set-Location's built-in alias cd for brevity):
powershell -command Start-Process -Verb RunAs -FilePath powershell -ArgumentList '-noexit -command cd \"\"\"%V%\"\"\"; . ''C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat'''
As an aside: PowerShell Core now has a -WorkingDirectory (-wd) CLI parameter that allows you to control the startup directory more robustly (pwsh -wd "c:\path\to\dir" ...); in fact, it was precisely the File Explorer custom context-menu use case that prompted the introduction of this parameter.

Running .cmd/.bat script in Powershell

I'm trying to write and execute a .cmd script in powershell. The code I have for this is:
$script = #'
#echo off
SETLOCAL
CALL something here
'#
Invoke-Expression -Command: $script
This is based off this link which explains the here string in powershell. It's at the bottom of the link. Here's the related msdn.
Here's another related link to someone trying to do the same thing.
I keep getting an error that has to do with including the '#' operator within the string:
Invoke-Expression : At line:1 char:7
+ #echo off
+ ~~~
Unexpected token 'off' in expression or statement.
At line:1 char:1
+ #echo off
+ ~~~~~
The splatting operator '#' cannot be used to reference variables in an expression. '#echo' can be used only as an argument to a command. To reference variables in an expression use '$echo'.
I've tried escaping the '#' symbol, and a plethora of other things. I'd like to know why it seemed to work for them in the third link, but throws this error in my case.
Edit:
Writing to a .bat file then running the bat file resulted in the same error:
$batchFileContent = #'
#echo off
c:\windows\system32\ntbackup.exe backup "C:\Documents and Settings\Administrator\Local Settings\Application Data\Microsoft\Windows NT\NTBackup\data\chameme.bks" /n "1file.bkf1 created 06/09/2013 at 09:36" /d "Set created 06/09/2013 at 09:36" /v:no /r:no /rs:no /hc:off /m normal /j chameme /l:s /f "\\fs1\Exchange Backups$\1file.bkf"
'#
$batchFileContent | Out-File -LiteralPath:"$env:TEMP\backup.cmd" -Force
Invoke-Expression -Command:"$env:TEMP\backup.cmd"
Remove-Item -LiteralPath:"$env:TEMP\backup.cmd" -Force
As Bill Stewart pointed out, I should write the content of the .cmd script in powershell.
Edit:
This
$script = #'
cmd.exe /C "#echo off"
cmd.exe /C "SETLOCAL"
cmd.exe /C "CALL something here"
'#
Invoke-Expression -Command: $script
Seems to work.
This happens because Invoke-Expression interprets your string with PowerShell. PowerShell allows you to run shell commands, but it interprets things as PowerShell first. The # character is the splatting operator in PowerShell.
You should save the commands in a batch file, and then execute that.
Or you can execute single line commands by shelling out to cmd.exe:
Invoke-Expression "cmd.exe /c #echo something"

Powershell opening file path with whitespaces

I'm my PS script I want to be able to run another script in another PS instance by doing the following:
$filepath = Resolve-Path "destruct.ps1"
start-process powershell.exe "$filepath"
destruct.ps1 is in the same folder as this script.
However when running this script in a location which includes spaces ("C:\My Scripts\") I will get the following error:
The term 'C:\My' is not recognized as a cmdlet, function, operable program, or script file. Verify the term and try again.
I know by using a '&' with the Invoke-Expression method solves this problem, how can I do the same but by using the start-process method?
try this:
start-process -FilePath powershell.exe -ArgumentList "-file `"$filepath`""
edit after comments:
start-process -FilePath powershell.exe -ArgumentList "-file `"$($filepath.path)`""
side note:
$filepath is a [pathinfo] type and not a [string] type.
You can add escaped double quotes so that you pass a quoted argument:
"`"$filepath`""
I am answering here for a general scenario.
If you need to navigate to a folder for example C:\Program Files from the Powerhsell, the following command won't work as it has white space in between the path.
cd C:\Program Files
Instead embed the path with double quotes as like the below.
cd "C:\Program Files"
File name might contain spaces, so preserve spaces in full path:
Notepad++ exec command:
"C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe" "& \"$(FULL_CURRENT_PATH)\""
same from command prompt:
"C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe" "& \"C:\a_work\Systems\name with spaces.ps1\""
Just in case [string]$shipno (which is path & file name) comes in including spaces the following allows it to be passed to -FilePath successfully:
if ($shipno.contains(" ") -eq $true) {
$shipno = """" + $shipno + """"
}

Invoke-Expression: Positional parameter cannot be found that accepts argument /s

I have a .ps1 script which contains a line
Invoke-Expression -Command "C:\Builds\$BuildName /s /v`"/l*v c:\build_install.txt /qn`""<br/>
This is performing Silent installation of a product.
Now, if I try to run this command from Linux box through ssh it gives the following error:
Invoke-Expression : A positional parameter cannot be found that accepts argument '/s'.
At line:1 char:1
+ Invoke-Expression C:\NWTBuilds\Setup-NimbleNWT-x64.2.0.4.117.exe /s /v`/l*v c:\n ...
+ CategoryInfo : InvalidArgument: (:) [Invoke-Expression], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.InvokeExpressionCommand
Do you have any suggestions on this? Do I need to provide any credentials?
So I have also tried the following options:
Send the command through ssh or telnet powershell.exe -Command ...
Call the powershell Script from ssh or telnet powershell.exe -File C:\Sample.ps1
However If I ran the same Sample.ps1 from windows Powershell, silent installation is done?
Your /s is being interpreted as being part of your Invoke-Expression call. Can you try Invoke-Command, i.e.:
Invoke-Command { C:\Builds\$BuildName /s /v "/l*v c:\build_install.txt /qn" }
The error message indicates that PowerShell is trying to parse /s as the name of a parameter of Invoke-Expression rather than as part of the argument supplied to -Command, which it would not do if the /s were part of the string. This implies that the string is being terminated just before that. Check the value of $BuildName, it probably contains something that terminates the string. I'm not quite sure what that might be, because a pair of double quotes within the variable value shouldn't have that effect. At least it wouldn't at a PowerShell prompt. Maybe the ssh client is interpreting what you're typing in some way that terminates the string before /s?
In any case, I'd be willing to bet money that the answer lies in the value of $BuildName, because logically the error indicates that the string argument to -Command terminates at that point.