Send "any key" to cmd.exe from powershell script - powershell

I have following PowerShell script:
cmd /c script1.bat
cmd /c script2.bat
script1.bat at the end of execution have "pause" command, so the execution of my PS script stops.
How can send any key cmd.exe to avoid stopping script execution?
NOTE: I can't change batch scripts - they are 3rd party.

You can pipe input to the program (cmd.exe) like this:
"X" | cmd /c script1.bat

You could put an empty file called pause.bat in the same directory. Then it would do nothing instead of pause.

The batch scripts may be 3rd party, but surely you can just a copy/backup and edit the content to remove the PAUSE command?
I sometimes put a PAUSE in if I am testing something and don't want the window to close, but otherwise I can't think of a good reason to keep that in.

I had trouble getting the accepted answer to work for me due having an expression in the bat file path.
"x" | $Env:WRAPPER_HOME\bat\installService.bat $LOGFILE
Error is "Expressions are only allowed as the first element of a pipeline."
Here's what I got working (finally):
[PS script code]
& runner.bat bat_to_run.bat logfile.txt
[runner.bat]
#echo OFF
REM This script can be executed from within a powershell script so that the bat file
REM passed as %1 will not cause execution to halt if PAUSE is encountered.
REM If {logfile} is included, bat file output will be appended to logfile.
REM
REM Usage:
REM runner.bat [path of bat script to execute] {logfile}
if not [%2] == [] GOTO APPEND_OUTPUT
#echo | call %1
GOTO EXIT
:APPEND_OUTPUT
#echo | call %1 1> %2 2>&1
:EXIT

Related

In Win10 Batch File's Arguments: Replacing UNC Path Names' prefixes with known mapped drive

I work in a shared drive on a server.
Sometimes people use its mapped drive, Q:\Folder\Subfolder\Etc\
Other times people use its UNC path: \\server.com\shared data\Folder\Subfolder\Etc\
I have a batch file that takes an argument of the paths of file(s) (usually multiple files) dropped onto it. It then passes those to a PowerShell script.
However, when someone drops files from a folder being access from its UNC path name, it barfs:
'\\server.com\shared data\Folder\Subfolder\Etc'
CMD.EXE was started with the above path as the current directory.
UNC paths are not supported. Defaulting to Windows directory.
How can I replace \\server.com\shared data\ with Q:\ for every filename in the argument? My PowerShell script does all the processing, so I want to fix it before it ever gets sent into PowerShell.
I get I can run 'cls' at the beginning of the beginning of the batch to clear away the warning.
I cannot modify the registry for this case.
#echo off
Title Pass arguments from this Batch script to PowerShell script of same name
rem
rem Batch Handler of Command Line Arguments for Microsoft Windows
rem Enables Passing Command Line Arguments to PowerShell Script
rem
rem The name of the script is drive path name of the Parameter %0
rem (= the batch file) but with the extension ".ps1"
set PSScript=%~dpn0.ps1
set args=%1
:More
shift
if '%1'=='' goto Done
set args=%args%, %1
goto More
:Done
powershell.exe -NoExit -Command "& '%PSScript%' '%args%'"
So, I'm hoping there is a way to keep the .ps1 PowerShell script from seeing any \\server.com\shared data\ and just let it see Q:\ instead.
This is what I ended up using: Enable Delayed Expansion was the ticket.
#echo off
setlocal enableDelayedExpansion
Title Sample Title (Initializing...)
rem
rem Batch Handler of Command Line Arguments for Microsoft Windows
rem Enables Passing Command Line Arguments to PowerShell Script
rem
rem
rem The name of the script is drive path name of the Parameter %0
rem (= the batch file) but with the extension ".ps1"
set "PSScript=%~dpn0.ps1"
set "args=%1"
:More
shift
if '%1'=='' goto Done
set "args=%args%, %1"
goto More
:Done
set "args=!args:\\server.com\shared data\=Q:\!"
if /i "!args!" == "\\server.com\shared data\=Q:\" ( set "args=" )
powershell.exe -NoExit -Command "& '%PSScript%' '%args%'"
Can’t try it right now, but I think you can try one of these:
Get all arguments’ names and parse \server.com\ to Q:\, not probably the best idea as each user could have different unit letter
If the files are on the same directory as the script (or always on same origin folder) try using pushd \\server.com\shared... to map the directory to a temp unit and then get it with cd to use it. When you are done, unmap the folder with popd (this will unmap last mapped unit)

Removal of Office

I'm currently trying to run a batch file as a startup script to detect and remove whatever version of office a user has installed and then to install Office 365. I have the install working however, when I attempted to uninstall Office 2013 I received the following error:
Input Error: Can not find script file "C:\Windows\OffScrub_O15msi.vbs"
The Offscrub file is in the same location as the script, is someone able to tell me why it's looking in C:\Windows for it?
Update
Please find my current script which now works for Office 2013, I previously added the line Remove2016Installs $true when using -Command to remove Office 2016, this worked. Since using -File to work around my initial problem I've been unable to get the script to remove Office 2016 and would like some advice on how to do this, I've read that whatever command is after -File needs to be the last which I believe might be why it's failing.
My full script is below:
start "----NOTICE----" cmd.exe /t:ec /Q /k "echo OFFICE 365 IS BEING INSTALLED. THIS WINDOW WILL CLOSE WHEN COMPLETE&&prompt $h"
#echo off
pushd "%~dp0"
powershell.exe -executionpolicy bypass -NoExit -File "Remove-PreviousOfficeInstalls.ps1"
popd
reg Query "HKLM\Hardware\Description\System\CentralProcessor\0" | find /i "x86" > NUL && set OS=32BIT || set OS=64BIT
if %OS%==32BIT "\\domain\SYSVOL\domain\Policies\{Policy Number}\Machine\Scripts\Startup\setup.exe" /configure "\\domain\SYSVOL\domain\Policies\{Policy Number}\Machine\Scripts\Startup\configuration-Office365-x86.xml"
if %OS%==64BIT "\\domain\SYSVOL\domain\Policies\{Policy Number}\Machine\Scripts\Startup\setup.exe" /configure "\\domain\SYSVOL\domain\Policies\{Policy Number}\Machine\Scripts\Startup\configuration-Office365-x64.xml"
taskkill /IM cmd.exe /FI "WINDOWTITLE EQ ----NOTICE----"
taskkill /IM cmd.exe /FI "WINDOWTITLE EQ Administrator: ----NOTICE----"
echo %date% %time% Setup ended with error code %errorlevel%. >> %LogLocation%\%computername%.txt
Update Finished
There's a line that calls the Powershell script Remove-PreviousOfficeInstalls, this is a file from GitHub that is very popular for the removal of whichever Office version you have installed.
I can run this command if say I copy these files to the desktop and amend the locations in the scripts, I'm not sure what this reference to C:\Windows is though when run from \domain\SYSVOL\domain\Policies{Policy Number}\Machine\Scripts\Startup\?
If you run a default instances of PowerShell it always starts in a certain directory. It depends on how and who it is started by. For instance an administrative PowerShell usually starts in C:\Windows\System32. If you use any paths that are not absolute they're applied relative to this directory.
To work around this you need to change the directory it's using. For instance by using cd to change the directory. My guess would be that your script Remove-PreviousOfficeInstalls.ps1 contains a relative call to the VBS.
An easy fix would be to run a script block instead of a single command and just cd to \\domain\SYSVOL\domain\Policies\'{Policy Number}'\Machine\Scripts\Startup\ prior to running the ps1.
The PowerShell help you can view by following [this] link or running powershell -h has the following information in regards to using the -Command switch.
...
Script blocks must be enclosed in braces ({}). You can specify a script block only when running PowerShell.exe in PowerShell. If you want to use a script block when running from another shell you must use the format:
"& {}"
...
The other important parameter for your use case is -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.
...
Your batch contains the following line:
powershell.exe -executionpolicy bypass -Command "\\domain\SYSVOL\domain\Policies\'{Policy Number}'\Machine\Scripts\Startup\Remove-PreviousOfficeInstalls.ps1 -Remove2016Installs $true"
What you do is run a single command to invoke a script with a parameter. The problems is that said script checks its locations based on certain function and with your invocation that location is wrongly detected.
There are multiple ways to fix this. One would be to change the directory before invoking the script. To do this you'd need to use a script block as the parameter for -Command. An example for this would be:
powershell.exe -Command "& {Write-Output 'Hello'; Write-Output 'World';}"
As you can see there are two independent Write-Output commands being run. You'd change this to a cd \\domain\SYSVOL\domain\Policies\'{Policy Number}'\Machine\Scripts\Startup\ and the invocation of your script. As a bonus you wouldn't need to put the whole path in front of the script anymore.
The other option would be to run powershell -File with your current invocation of the script. That should also mean that the script is read from the file and the corresponding parameters are populated accordingly.
If neither of these options work you will have to check what $PSScriptRoot is being populated with and/or what the return of (Get-Item -Path ".\").FullName is as those are the two commands used to determine the location of the script that's being executed. To do this you could use a script block.
thanks for your help regarding this. My resolution was to use the following bat command:
`start "----NOTICE----" cmd.exe /t:ec /Q /k "echo OFFICE 365 IS BEING INSTALLED. THIS WINDOW WILL CLOSE WHEN COMPLETE&&prompt $h"`
#echo off
pushd "%~dp0"
powershell.exe -executionpolicy bypass -File Remove-PreviousOfficeInstalls.ps1 -Remove2016Installs
popd
reg Query "HKLM\Hardware\Description\System\CentralProcessor\0" | find /i "x86" > NUL && set OS=32BIT || set OS=64BIT
if %OS%==32BIT "\\Server\Folder\Folder\setup.exe" /configure "\\Server\Folder\Folder\configuration-Office365-x86.xml"
if %OS%==64BIT "\\Server\Folder\Folder\setup.exe" /configure "\\Server\Folder\Folder\configuration-Office365-x64.xml"
taskkill /IM cmd.exe /FI "WINDOWTITLE EQ ----NOTICE----"
taskkill /IM cmd.exe /FI "WINDOWTITLE EQ Administrator: ----NOTICE----"
echo %date% %time% Setup ended with error code %errorlevel%. >> %LogLocation%\%computername%.txt
I had to amend the Remove-PreviousOfficeInstalls powershell script to include the switch command:
[Parameter(ValueFromPipelineByPropertyName=$true)]
[switch]$Remove2016Installs = $false,
This then did exactly what I was after, it detected the current version of Office, removed it and installed the correct bit version of Office 365 for that PC\Laptop.
Thanks for all your help

How to simulate a batch script

I need to setup a PowerShell script based upon a batch script. The original batch script looks like the following:
call %SYSTEMROOT%\setup_Env.BAT
command_name command_arguments
The command is dependent upon the environmental variables from the setup_ENV.BAT being setup.
$tempFile = [IO.Path]::GetTempFileName()
$script = "%SYSTEMROOT%\setup_Env.BAT"
cmd /c " $script && set > $tempFile "
cmd /c " command_name command_arguments"
I received the error:
cmd : 'command_name is not recognized as an internal or external command,...
If there is a better way to do this in PowerShell, I am open to it.
You need to pass a single command line to cmd to make this work:
cmd /c "call %SYSTEMROOT%\setup_Env.BAT && command_name command_arguments"
As Ansgar Wiechers points out, every cmd invocation runs in a child process, and any environment modifications made in a child process are not visible to the calling process and therefore also not to future child processes.
By contrast, in the single command line above, the environment-variable modifications performed by setup_Env.BAT are visible to command_name by the time it executes.
Caveat: If command_arguments contains %...%-style references to the environment variables defined in setup_Env.BAT, more work is needed:
Change the %...%-style references to !...!-style references.
Additionally invoke cmd with /v to enable delayed variable expansion (the equivalent of setlocal enabledelayedexpansion inside a script`:
cmd /v /c "call %SYSTEMROOT%\setup_Env.BAT && command_name args_with_delayed_var_refs"
Caveat: The above may still not work as intended if command_arguments happens to contain ! chars. that should be treated as literals (and/or command_name is another batch file containing such).
In that event, the simplest approach is to simply recreate the entire batch file in a temporary file and invoke that:
# Get temp. file path
$tempBatFile = [IO.Path]::GetTempFileName() + '.bat'
# Write the content of the temp. batch file
#'
#echo off
call %SYSTEMROOT%\setup_Env.BAT
command_name command_arguments
'# | Set-Content $tempBatFile
# Execute it.
& $tempBatFile
# Clean up.
Remove-Item -LiteralPath $tempBatFile

how to run a powershell script and a batch script in a batch script?

i have controller batch file named oneclick.bat, code as below:
rem batch controller
rem wait for email input for ssh key generation...
rem call copyEnv.bat
call generateSshKey.bat %1
call gitClone.bat
in generateSshKey.bat, i start a powershell script like this:
rem make sure powershell is able to run
powershell.exe set-executionpolicy remotesigned
rem start powershell and add ssh key to the server by ui aotumation
powershell.exe -noe -file SetSSHKeytoServer.ps1
and then gitClone.bat did not run in the command window
how can i get the gitClone.bat run?
When you want to return to a calling script you shouldn't run PowerShell with an option that prevents it from returning. Remove -noe from the last line of generateSshKey.bat.
-NoExit
Does not exit after running startup commands.
As Ansgar Wiechers pointed out, the -noe (short for -NoExit) is keeps the PowerShell session open, so generateSshKey.bat doesn't exit, and oneclick.bat doesn't get past the line that called it.
If you specifically want the powershell session that runs SetSSHKeytoServer.ps1 to remain active (since you used the -noe switch the second time but not the first time, I'm inferring that this was deliberate), you could change
call generateSshKey.bat %1
to
start generateSshKey.bat %1
Note that this means that oneclick.bat won't wait for generateSshKey.bat to finish before calling gitClone.bat, so if you need to run them in sequence, this won't work. It will work as long as it's okay for gitClone.bat to start while generateSshKey.bat is still running.

How can I detect whether or not I am in powershell from a command line?

I am creating a standard windows BAT/CMD file and I want to make an IF statement to check whether or not this CMD file is run from PowerShell. How can I do that?
Edit: My underlying problem was that test.cmd "A=B" results in %1==A=B when run from CMD but %1==A and %2==B when run from PowerShell. The script itself is actually run as an old Windows Command line script in both cases, so checking for Get-ChildItem will always yield an error.
One way, it to see what your process name is, and then check its attributes:
title=test
tasklist /v /fo csv | findstr /i "test"
As long as you use a unique name in place of Test, there should be little room for error.
You will get back something like:
"cmd.exe","15144","Console","1","3,284
K","Running","GNCID6101\Athomsfere","0:00:00","test"
When I ran the above code from a bat file, my output was:
"powershell.exe","7396","Console","1","50,972
K","Running","GNCID6101\Athomsfere","0:00:00","
A potentially simpler approach that may work for you. If not, it may be suitable for others.
Create 2 separate script files: test.ps1 and test.cmd
Don't include extension when calling the script. I.e. call as <path>\test (or just test if folder is in the path environment variable.
This works because CMD prioritises which script to execute as: .bat > .cmd, whereas Powershell prioritises: .ps1 > .bat > .cmd.
The following is the output of a CMD session:
C:\Temp>copy con test.cmd
#echo cmd^Z
1 file(s) copied.
C:\Temp>copy con test.ps1
Write-Output "ps1"^Z
1 file(s) copied.
C:\Temp>.\test
cmd
C:\Temp>
And calling test from Powershell:
PS C:\Temp> .\test
ps1
PS C:\Temp>
Couldn't you try to execute a Get-ChildItem and then check %ERRORLEVEL% to see if it returns an exe not found?
http://ss64.com/nt/errorlevel.html