passing \ in argument to powershell script causes unexpected escaping - powershell

This is my powershell script test.ps1:
Write-Output $args;
Now suppose I have a batch script that calls this powershell script with all kinds of paths. One of those is c:\:
powershell -executionpolicy Bypass -file test.ps1 "c:\"
The output is:
c:"
Is there any way to quote my arguments such that c:\ would actually be taken and stored as is in the $args[0] variable? I know I can solve this quick'dirty by passing "c:\\", but that's not a real solution.
EDIT: using named parameters in test.ps1 doesn't make any difference:
[CmdletBinding()]
param(
[string]$argument
)
Write-Output $argument;
EDIT2: using a batch file instead works fine.
My test.bat script:
echo %~1
I run it:
test.bat "c:\"
Returns nicely:
c:\

Are you sure this comes form powershell and not from the program which invokes your statement? The backslash is no escape code in powershell.
my test.ps1 is working, when run from ise.
this works for me:
powershell -executionpolicy Bypass -command "test.ps1 -argument 'C:\'"
(end with quote double-quote)

Help file for PowerShell.exe says:
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.
You are against Powershell.exe's command line parser, which uses "\" to escape quotes. Do you need quotes? Not in your case:
powershell -file test.ps1 c:\
prints
c:\
Similarly, this works too
powershell -file test.ps1 "c:\ "
c:\
but then your arg has that extra space which you would want to trim. BTW, Single quotes do not help here:
powershell -file test.ps1 'c:\'
'c:\'

If you need the final backlash to be passed to the command, you can use
$ArgWithABackslashTemp = $ArgWithABackslash -replace '\\$','\\'
&$ExePath $ArgWithABackslashTemp
Or, if the exe is smart enough to handle it without the trailing backslash
&$ExePath $ArgWithABackslash.trim('\')

Related

Multiline powershell function inside batch script

I want to run .bat-script which calls some powershell function inside it. Function is not so small, so I want to split it. But I cannot do it, escape symbols doesn`t help ( ` ,^).
Script example:
set file=%1
set function="$file=$Env:file; ^
$hash = CertUtil -hashfile $file SHA256 | Select -Index 1"
powershell -command %function%
You can leave the quote at the end of each line like so:
set file=%1
set function="$file=$Env:file; "^
"$hash = CertUtil -hashfile $file SHA256 | Select -Index 1; "^
"example break line further...."
powershell -command %function%
The ^ works as multiline character but it also escapes the first character, so also a quote would be escaped.
Do not mix batchfile syntax with PowerShell. As #Stephan mentioned $function= won't work in batch file. You need to use set function= instead. Let's say I want to execute the following:
Get-Process
Get-ChildItem
Then the code should look like this:
set function=Get-Process; ^
Get-ChildItem;
And you start PowerShell with:
powershell -noexit -command %function%
-noexit added so that you can verify that the code was successfully executed.
Also keep in mind that what you pass to PowerShell is batch multiline and in PowerShell it's visible as one line so you have to remember about semicolon (which you actually do but I'm leaving this comment here for future readers).
There's also another option how to pass variable from batch script to PowerShell. You can do it like this:
set name=explorer
set function=get-process $args[0]; ^
get-childitem
powershell -noexit -command "& {%function% }" %name%
Explanation:
$args[0] represents first argument passed to the scriptblock. To pass that argument, add %name% after the scriptblock while starting powershell. Also, as pointed out in this answer (credits to #Aacini for pointing this out in comments), you have to add & operator and keep your scriptblock inside curly brackets { }.
Sidenote: to be honest, I'd avoid running scripts like this. Much simpler way would be to just save the file as .ps1 and run this in your batch file:
powershell -noexit -file .\script.ps1

How can I run a PowerShell script with white spaces in the path from the command line?

So I've tried a bunch of different ways to run a PowerShell script from the command line and every single one returns an error.
Here is this path:
C:\Users\test\Documents\test\line space\PS Script\test.ps1
I've tried these:
powershell -File '"C:\Users\test\Documents\test\line space\PS Script\test.ps1"'
powershell "& ""C:\Users\test\Documents\test\line space\PS Script\test.ps1"""
Powershell "& 'C:\Users\test\Documents\test\line space\PS Script\test.ps1'"
Powershell -File 'C:\Users\test\Documents\test\line space\PS Script\test.ps1'"
I get all these errors:
& : The term 'C:\Users\test\Documents\test\line space\PS Script' 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.
Processing -File ''C:\Users\test\Documents\test\line space\PS Script'' failed: The given path's format is not support ed. Specify a valid path for the -File parameter.
How can I fix this?
The -File parameter
If you want to run powershell.exe -File from the command line, you always have to set paths with spaces in double quotes ("). Single quotes (') are only recognized by PowerShell. But as powershell.exe is invoked (and hence the file parameter processed) by the command line, you have to use ".
powershell.exe -File "C:\Users\test\Documents\Test Space\test.ps1" -ExecutionPolicy Bypass
The -Command parameter
If you use the -Command parameter, instead of -File, the -Command content is processed by PowerShell. Hence you can - and in this case have to - use ' inside ".
powershell.exe -Command "& 'C:\Users\test\Documents\Test Space\test.ps1'" -ExecutionPolicy Bypass
The double quotes are processed by the command line, and & 'C:\Users\test\Documents\Test Space\test.ps1' is a command that is actually processed by PowerShell.
Solution 1 is obviously simpler.
Note that -Command is also the default parameter that is used, if you do not specify any.
powershell.exe "& 'C:\Users\test\Documents\Test Space\test.ps1'" -ExecutionPolicy Bypass
This would work, too.
The -EncodedCommand parameter
You can encode your command as Base64. This solves many "quoting" issues and is sometimes (but not in your case though) the only possible way.
First you have to create the encoded command
$Command = "& 'C:\Users\test\Documents\Test Space\test.ps1'"
[Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($Command))
And then you can use the the -EncodedCommand parameter like this
powershell.exe -EncodedCommand JgAgACcAQwA6AFwAVQBzAGUAcgBzAFwAdABlAHMAdABcAEQAbwBjAHUAbQBlAG4AdABzAFwAVABlAHMAdAAgAFMAcABhAGMAZQBcAHQAZQBzAHQALgBwAHMAMQAnAA== -ExecutionPolicy Bypass
Try this:
& "C:\Users\test\Documents\test\line space\PS Script\test"
In your examples, you're mixing quotes and double quoting for no reason.
IF EXIST "C:\Users\test\Documents\test\line space\PS Script\test.ps1" (
powershell -ExecutionPolicy Unrestricted -File "C:\Users\test\Documents\test\line space\PS Script\test.ps1"
)
In case you use parameters you can do as follows.
powershell.exe -command "& {&'C:\A B C\foo.ps1' param1 param2}"
Thanks at this point to a blog post by Hesham A. Amin :-)
I needed to pass a parameter with spaces.
I am dragging and dropping a file onto a batch file, and the file is off on the server with spaces in the path and/or file name. After testing the above answers, I got this to work. Note I am changing to the working directory prior to starting the PowerShell executable.
Batch file:
pushd O:\Data\QuickList
start powershell -noexit -Command ".\QuickList.ps1 -datafile '%1'"
popd

Powershell Inkove-expression

Yesterday I read about the invoke-expression
from
http://technet.microsoft.com/en-us/library/dd347550.aspx
invoke-expression -command "c:\batfile.bat"
It works very well, but how can I get the results and also , how to add args for .bat file?
Invoke-Expression is to Call up the EXpression,And '.Bat' is not an expression.
So if you want to call .Bat File Justc:\batfile.bat Could Do the trick.
Or if you want to explore on the invoke-expression Copy the commands in Batch file into a variable to see the code being executed.
There was similar instance where I had to do something similar,And I had used it as
$prog="cmd.exe"
$TARGETDIR = 'd:\temp'
$x='C:\Users\v-chetak\Desktop\1.bat'
$params=#("/C";"$x";" >d:\temp\result17.txt")
Start-Process -Verb runas $prog +$params
The $params Will Hold all the arguments you want to pass.

Capturing different streams in file

I'm trying to capture the Verbose, Error and other streams of a PowerShell script in a file. This to monitor the output of my script.
The following code works fine:
$LogFile = 'S:\ScriptLog.log'
$ScriptFile = 'S:\TestieScript.ps1'
powershell -Command $ScriptFile *>&1 > $LogFile
However, the moment I try to put a space in one of the file paths, it's no longer working. I tried a lot of things, like double quotes, single quotes, .. but no luck.
To illustrate, the following code doesn't work:
$LogFile = 'S:\ScriptLog.log'
$ScriptFile = 'S:\Testie Script.ps1'
powershell -Command $ScriptFile *>&1 > $LogFile
One person in this thread has the same issue.
Thank you for your help.
You're trying to run a file whose name contains a space as a command without proper quoting, so you're most likely getting an error like this in your log:
The term 'S:\Testie' is not recognized as the name of a cmdlet, function, script file, or operable program.
Either add proper quoting (and the call operator &, because your path is now a string):
powershell -Command "& '$ScriptFile'" *>&1 > $LogFile
or (better) use the -File parameter, as #CB. already suggested:
powershell -File $ScriptFile *>&1 > $LogFile
which has the additional advantage that the call will return the actual exit code of the script.
Edit: If you want to run the command as a scheduled task you'll need to use something like this:
powershell -Command "& 'S:\Testie Script.ps1' *>&1 > 'S:\ScriptLog.log'; exit $LASTEXITCODE"
because the redirection operators only work inside a PowerShell process.
try using -file parameter:
powershell -file $ScriptFile *>&1 > $LogFile

Calling powershell function with parameter from .cmd or .bat file

I have written a powershell script which is a complete function taking parameters (e.g. function name (param) { } ) and below this is a call to the function, with the parameter.
I want to be able to call this function in its .ps1 file, passing in the parameter. How would I be able to package a call to the function via a .bat or .cmd file? I am using Powershell v2.0.
You should use so called "dot-sourcing" of the script and the command with more than one statement: dot-sourcing of the script + call of the function with parameters.
The test script Test-Function.ps1:
function Test-Me($param1, $param2)
{
"1:$param1, 2:$param2"
}
The calling .bat file:
powershell ". .\Test-Function.ps1; Test-Me -Param1 'Hello world' -Param2 12345"
powershell ". .\Test-Function.ps1; Test-Me -Param1 \"Hello world\" -Param2 12345"
Notes: this is not a requirement but I would recommend enclosing the entire command text with double quotation marks escaping, if needed, inner quotation marks using CMD escape rules.
I believe all you have to do is name the parameters in the call to the script like the following:
powershell.exe Path\ScripName -Param1 Value1 -Param2 Value2
Param1 and Param2 are actual parameter names in the function signature.
Enjoy!
To call a PowerShell function from cmd or batch with arguments you need to use the -Commmand Parameter or its alias -C.
Romans answer will work with PowerShell 5.1 for example but will fail for PowerShell 7.1.
Quote from an issue I left on GitHub on why the same command didn't work is:
So as to support Unix shebang lines, pwsh's CLI now defaults to the -File parameter (which expects only a script-file path), whereas powershell.exe default to -Command / -c.
To make your commands work with pwsh, you must use -Command / -C explicitly.
So if you have a PowerShell file test.ps1 with:
function Get-Test() {
[cmdletbinding()]
Param (
[Parameter(Mandatory = $true, HelpMessage = 'The test string.')]
[String]$stringTest
)
Write-Host $stringTest
return
}
And the batch file will then be:
rem Both commands are now working in both v5.1 and v7.1.
rem v7.1
"...pathto\pwsh.exe" -NoExit -Command ". '"...pathto\test.ps1"'; Get-Test ""help me"""
rem v5.1
powershell.exe -NoExit -Command ". '"...pathto\test.ps1"'; Get-Test ""help me"""
The quotes around ...pathto\test.ps1 are a must if your .ps1 contains spaces.
The same goes for ...pathto\pwsh.exe
Here's the Github issue I posted in full:
https://github.com/PowerShell/PowerShell/issues/15281