Why does a second parameter cause this script to fail? - windows

I have three bat files in Windows 7
1) main.bat:
test testEcho parm1 parm2
2) test.bat:
#echo With one parm
"C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe" -nologo -noprofile ^
-executionpolicy bypass -command "& {start-process -verb 'runas' -file %1.bat %2}"
#echo With two parms
"C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe" -nologo -noprofile ^
-executionpolicy bypass -command "& {start-process -verb 'runas' -file %1.bat %2 %3}"
pause
3) testEcho.bat:
#echo ==== 0:%0 1:%1 2:%2
pause
When I run main.bat, it passes the name of the target bat file (testEcho) and two parameters. Then I use PowerShell twice to run the passed in bat file name (%1 = testEcho). The first time is with one parameter (%2 = parm1) and the second time with two parameters (%2 = parm1, %3 = parm2).
The first time works as expected, running testEcho.bat with one parameter. It echos the result:
==== 0:C:\testEcho.bat 1:parm1 2:
The second run fails with error:
"Start-Process: A positional parameter cannot be found that accepts
argument 'parm2'.At line:1 char:4"
Why does the second attempt fail and how can I get it to work? The only difference between them is that the first attempt passes only one parameter and the second attempt passes more than one parameter.

You need to specify an ArgumentList in order to use multiple arguments. These arguments need to be delimited by commas. http://ss64.com/ps/start-process.html
#echo With one parm
"C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe" -nologo -noprofile ^
-executionpolicy bypass -command "& {start-process -verb 'runas' -file %1.bat %2}"
#echo With two parms
"C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe" -nologo -noprofile ^
-executionpolicy bypass -command "& {start-process -verb 'runas' -file %1.bat -ArgumentList %2, %3}"
pause

Related

Split string in Windows batch file over multiple lines

I want to invoke an elevated Powershell to execute a script and pass a bunch of parameters. I would like to have every parameter in the .bat file on its own line. Usually I can use the carat ^ to span commands over several lines in .bat files, just like using a grave accent ` in Powershell scripts. But both don't work in this situation:
As a one-liner it works:
Powershell.exe -Command "& {Start-Process Powershell.exe -Verb RunAs -ArgumentList '-ExecutionPolicy Bypass -File %~dp0HelloWorld.ps1 -parameter1 Long -parameter2 list -parameter3 of -parameter4 parameters' }"
Trying to split it up into multiple lines using a caret ^ doesn't work:
Powershell.exe -Command "& {Start-Process Powershell.exe -Verb RunAs -ArgumentList '-ExecutionPolicy Bypass -File %~dp0HelloWorld.ps1 ^
-parameter1 Long ^
-parameter2 list ^
-parameter3 of ^
-parameter4 parameters ^
'}"
Here is an example HelloWorld.ps1 to test with, (has to be in the same directory as the batch file):
param (
$parameter1="",
$parameter2="",
$parameter3="",
$parameter4=""
)
write-host "$parameter1 $parameter2 $parameter3 $parameter4"
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
The simplest way to handle the issue in your example case above, is to use doublequotes, " instead of single, '. However to prevent cmd.exe from failing to parse the command, you'll need to escape those nested doublequotes, using backslashes, i.e. \".
Example:
#Powershell.exe -Command "& {Start-Process Powershell.exe -Verb RunAs -ArgumentList \" ^
-ExecutionPolicy RemoteSigned ^
-File `\"%~dp0HelloWorld.ps1`\" ^
-parameter1 Long ^
-parameter2 list ^
-parameter3 of ^
-parameter4 parameters ^
\"}"
I have specifically changed the ExecutionPolicy from Bypass, (which I wouldn't ever recommend using, even moreso when running elevated), to RemoteSigned, please do not change it back. Also for added safety, I have quoted your .ps1 file path, which could contain spaces. The backslashes escape the doublequotes for the cmd.exe parser, (as already mentioned), and then backticks escape the remaining nested doublequotes for the powershell.exe parser.

Batch script to run Powershell script: Flashing window

System: Windows 10
Powershell Version: 5.1
Purpose: Run a powershell script from a batch file
Parameters: Directory, Filename, Server, Username, Password all being passed in as string encompassed in "" when called from command to handle the situation when there is a space (such as the Directory which currently has a space in)
The Powershell script works perfectly, it creates the credential and starts the RDP connection without issues. When calling from a batch file however the Powershell window flashes and closes immediately. Command Prompt window is run as Administrator.
I've tried using:
- pause
- -noexit
- code from https://blog.danskingdom.com/allow-others-to-run-your-powershell-scripts-from-a-batch-file-they-will-love-you-for-it/
if ($Host.Name -eq "ConsoleHost")
{
Write-Host "Press any key to continue..."
$Host.UI.RawUI.FlushInputBuffer() # Make sure buffered input doesn't "press a key" and skip the ReadKey().
$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyUp") > $null
}
Clicking on the appearing Powershell window (To try get the select function to pause the window).
None of which have worked.
The code in question is:
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& {Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File """"%PowerShellScriptPath%"""" """"%server%"""" """"%username%"""" """"%password%""""' -Verb RunAs}";
Outside of this code no changes have been made to the system, the four speech marks were marked as required to pass through parameters and can be found under the blog post link above at the bottom just before the comments.
There is a high chance i'm using this incorrectly, I am a novice to batch and even newer to powershell. The batch scripts are being made as internal as possible, they need to be able to be used by a base install of Windows. They will eventually be migrated onto versions of Windows Server 2008 and up.
Is there anything that needs to be done with command prompt to allow it to run Powershell code?
Is the powershell code correct for the purpose i'm intending to use it for?
Is there any way, besides the ones listed, to view error, log information or pause the powershell window when run from a batch script?
Any input would be really appreciated!
Edit:
-NoExit variations:
Parent Call
PowerShell **-NoExit** -NoProfile -ExecutionPolicy Bypass -Command "& {Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File """"%PowerShellScriptPath%"""" """"%server%"""" """"%username%"""" """"%password%""""' -Verb RunAs}";
Nested call
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& {Start-Process PowerShell -ArgumentList '**-NoExit** -NoProfile -ExecutionPolicy Bypass -File """"%PowerShellScriptPath%"""" """"%server%"""" """"%username%"""" """"%password%""""' -Verb RunAs}";
Parent and Nested
PowerShell **-NoExit** -NoProfile -ExecutionPolicy Bypass -Command "& {Start-Process PowerShell -ArgumentList '**-NoExit** -NoProfile -ExecutionPolicy Bypass -File """"%PowerShellScriptPath%"""" """"%server%"""" """"%username%"""" """"%password%""""' -Verb RunAs}";
Variables set inside batch script as:
SET server=%~1
To remove the quotation marks I have also tried using:
SET server=%1
SET server=%server:"=%

Keep batch script running after launching powershell within

I am using a batch script to automate a lot of tasks I have to run on a computer.
In the batch file I have a menu making you choose between each task separately or do everything.
When I choose to do everything, after launching the first .ps1 it just kills my cmd window.
I found some way to keep the window open but the script doesn't continue...
Also I created a "master file" to call all the ".ps1" and tried those cmds: call, /wait
The call feature works and they all start. So I don't understand why they would do the same in my script.
The cmd lines bellow are not bellow each other in my script because I want to be able to call them separately.
Part of the script I would like to run properly:
echo Windows Layout
start /wait PowerShell.exe -Command "& {Start-Process PowerShell.exe -ArgumentList '-ExecutionPolicy Bypass -File ""%~dp0Customisation\Export_Import_Layout.ps1""' -Verb RunAs}"
echo Remove Unwanted Apps
start /wait PowerShell.exe -Command "& {Start-Process PowerShell.exe -ArgumentList '-ExecutionPolicy Bypass -File ""%~dp0Clean\RemoveAppx.ps1""' -Verb RunAs}"
wmic product where name="WinZip 22.5" call uninstall
ping -n 6 localhost >nul
Taskkill /IM /F "MicrosoftEdge.exe"
Taskkill /IM /F "MicrosoftEdgeCP.exe"
Taskkill /IM /F "MicrosoftEdgeSH.exe"
%~dp0Clean\MCPR.exe
The master.bat looks like this :
call PowerShell.exe -Command "& {Start-Process PowerShell.exe -ArgumentList '-ExecutionPolicy Bypass -File ""%~dp0InstallChocoAndApps.ps1""' -Verb RunAs}"
call PowerShell.exe -Command "& {Start-Process PowerShell.exe -ArgumentList '-ExecutionPolicy Bypass -File ""%~dp0Clean\RemoveAppx.ps1""' -Verb RunAs}"
Any hint would be appriciated.
Please keep in mind I am not a script specialist.
Thanks
Ben
Managed to solve my issue by adding start "" /wait instead of start /wait

How capture errorlevel of powershell script in a batch file when execute as admin

I need to capture output of powershell script in a batch file when execute as admin.
Example:
ps1 file:
Write-Host "PS1 executed"
exit 1
If I execute powershell script without admin access
NotAdminFile.bat:
#ECHO OFF
setlocal enableextensions
PowerShell.exe -NoProfile -ExecutionPolicy Bypass -Command "& '%~dpn0.ps1'"
echo %ERRORLEVEL%
endlocal
Then, the output is
PS1 executed
1
This is ok.
But, when I execute powershell script with admin access
AdminFile.bat:
#ECHO OFF
setlocal enableextensions
PowerShell.exe -NoProfile -Command "& {Start-Process PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File "%~dpn0.ps1" ' -Verb RunAs}"
echo %ERRORLEVEL%
endlocal
Then, the output is:
0
I don't want that. Can you help me please?
exit_1_only.ps1
Write-Host "executed: $($MyInvocation.Line)"
# pause
Exit 123+[int]([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::
GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]"Administrator")
q44600354.bat
#ECHO OFF
SETLOCAL EnableExtensions DisableDelayedExpansion
(call )
echo errorlevel clear=%errorlevel%
PowerShell.exe -NoProfile -ExecutionPolicy Bypass -Command ^
"& 'D:\PShell\tests\exit_1_only.ps1'; exit $LASTEXITCODE"
echo errorlevel non-admin=%errorlevel%
echo(
(call )
echo errorlevel clear=%errorlevel%
PowerShell.exe -NoProfile -ExecutionPolicy Bypass -Command ^
"& {exit ( Start-Process -Wait -PassThru -FilePath PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -Command ""D:\PShell\tests\exit_1_only.ps1; exit $LASTEXITCODE"" ' -Verb RunAs).ExitCode}"
echo errorlevel admin=%errorlevel%
Output
==> D:\bat\SO\q44600354.bat
errorlevel clear=0
executed: & 'D:\PShell\tests\exit_1_only.ps1'; exit $LASTEXITCODE
errorlevel non-admin=123
errorlevel clear=0
errorlevel admin=124
==>
Explanation
Exit Codes
In PowerShell $? contains True if last operation succeeded and
False otherwise.
The exit code of the last Win32 executable execution is stored in the
automatic variable $LASTEXITCODE
To read exit codes (other than 0 or 1) launch the PowerShell
script and return the $LASTEXITCODE in a single line like this:
powershell.exe -noprofile C:\scripts\script.ps1; exit $LASTEXITCODE
Start-Process
-PassThru
Returns a System.Diagnostics.Process process object for each process
that the cmdlet started. By default, this cmdlet does not generate any
output.
-Wait
Indicates that this cmdlet waits for the specified process to complete
before accepting more input. This parameter suppresses the command
prompt or retains the window until the process finishes.
(call ): Dave Benham in reply to setting ERRORLEVEL to 0 question:
If you want to force the errorlevel to 0, then you can use this
totally non-intuitive, but very effective syntax: (call ). The space
after call is critical. If you want to set the errorlevel to 1,
you can use (call). It is critical that there not be any space after
call.

How to pass variable from Batch File to a Powershell Script

I have a batch file that copies user files from one computer to another networked computer.
#echo off
cls
#echo Type the old Computer Name
set /p asset=
REM robocopy.exe \\%asset%\c$\ C:\ /S /Z /XJD /XJ /XA:SH /XA:T /XD "Dir1" "Dir2" /XF *.dll *.log *.txt *.exe /log+:"\\server\path\%asset%-to-%computername%-Transfer.log" /NP /FP /V /TEE
PowerShell.exe -Command "& {Start-Process PowerShell.exe -ArgumentList '-ExecutionPolicy Bypass -File ""%~dpn0.ps1""' -Verb RunAs}"
pause
This is my PowerShell script:
$source = "\\${env:asset}\C$\Temp\Source"
$dest = "C:\Temp\Dest"
Write-Output $source
Read-Host "Press ENTER to quit"
I then need to call a PowerShell script that invokes an admin login, then pass the %asset% and %useraiu% variables.
I can't seem to figure out how to pass the %asset% and %useraiu% from my batch file to the PowerShell script.
I have found that if you are calling the Powershell script from a batch file and need to have the Powershell script run with admin, you would need to use this syntax.
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& {Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File """"%~dpn0.ps1"""" """"%asset%"""" ' -Verb RunAs}"
The PowerShell script name and parameters need to be wrapped in 4 double quotes in order to properly handle paths/values with spaces
This is the only solution that worked for me so far.

Resources