Send commands to a cmd window through a .bat file - Rejected StackOverflow question
Posted: 10 Apr 2016 10:47
Here is a rejected question from StackOverflow: http://stackoverflow.com/q/36527988/1012053
There are various free Windows utilities available that allow you to send key presses, mouse events, etc. to any window of your choosing. One example is AutoIT - https://www.autoitscript.com/site/autoit/
But I have developed a pure batch solution that is fairly simple to implement. My solution assumes that the controlling batch script is to assume total control of the server window, and the server window is simply looking for commands from stdin.
My demo script simply uses cmd.exe as the "server", but any console based exe could be used.
Your controller script can write commands to a commandFile, and cmd.exe can use redirection to read the commands from the file. But cmd.exe will exit as soon as it reaches the end of file, so something a bit more sophisticated is required.
The solution is to have your server window executing a server batch script that reads the file using SET /P in a loop, and pipes the commands to the actual server. The trick is that SET /P reading from a file will simply return nothing if it is at end of file, without moving the input stream file pointer, and without invalidating the input stream. So if a new line is appended to the file, a subsequent SET /P can properly read it. I did something similar with my TEE.BAT utility.
Note that SET /P is limited a maximum line of 1021 characters. Since I append an extra dummy character to the end of each line, the maximum command length is 1020 characters.
I define a command that will signal the server batch script to quit. The cmd.exe program will automatically close if its input stream is closed, so my server batch script could simply EXIT once it detects the defined quit command. But I'm not sure that all console programs behave that way, so I let the server batch script send a command to the server exe process to quit.
The only other thing is to setup a controller batch script that launches the server, and then enters a loop that sends a command every 5 minutes. Well I didn't want to wait 5 minutes between commands, so my demo waits only 5 seconds between commands. Your controller could be arbitrarily complex. I opted to simply read a list of static commands that are embedded within the script, waiting 5 seconds between each command. The command is sent by simply appending it to the command file that the server is reading. I append an extra character to the end of each command so that I can send an empty string. The server batch script strips off the trailing character before piping it to the actual server.
I could have used two separate batch scripts, one for the controller, and one for the server. But I opted to put everything in a single script. The demo script should be called without any parameters. The script STARTs a new server window that calls itself, but this time with a :Server parameter. The :Server parameter effectively instructs the script to branch to the server code.
When you run the demo script, it will open a new server window. The controller window enters a loop that waits 5 seconds and then reads the next command and sends it. The server window will read and execute each command that is sent. The server window will automatically close once it receives the EXIT command.
Dave Benham
StackOverflow user Bogdan Paicu wrote:So basically i have this Unturned server that's running in a CMD type window. I can input commands to control the server in that window. What I'm trying to do is set up a .bat file that sends commands to the CMD window of the server every, say, 5 minutes.
There are various free Windows utilities available that allow you to send key presses, mouse events, etc. to any window of your choosing. One example is AutoIT - https://www.autoitscript.com/site/autoit/
But I have developed a pure batch solution that is fairly simple to implement. My solution assumes that the controlling batch script is to assume total control of the server window, and the server window is simply looking for commands from stdin.
My demo script simply uses cmd.exe as the "server", but any console based exe could be used.
Your controller script can write commands to a commandFile, and cmd.exe can use redirection to read the commands from the file. But cmd.exe will exit as soon as it reaches the end of file, so something a bit more sophisticated is required.
The solution is to have your server window executing a server batch script that reads the file using SET /P in a loop, and pipes the commands to the actual server. The trick is that SET /P reading from a file will simply return nothing if it is at end of file, without moving the input stream file pointer, and without invalidating the input stream. So if a new line is appended to the file, a subsequent SET /P can properly read it. I did something similar with my TEE.BAT utility.
Note that SET /P is limited a maximum line of 1021 characters. Since I append an extra dummy character to the end of each line, the maximum command length is 1020 characters.
I define a command that will signal the server batch script to quit. The cmd.exe program will automatically close if its input stream is closed, so my server batch script could simply EXIT once it detects the defined quit command. But I'm not sure that all console programs behave that way, so I let the server batch script send a command to the server exe process to quit.
The only other thing is to setup a controller batch script that launches the server, and then enters a loop that sends a command every 5 minutes. Well I didn't want to wait 5 minutes between commands, so my demo waits only 5 seconds between commands. Your controller could be arbitrarily complex. I opted to simply read a list of static commands that are embedded within the script, waiting 5 seconds between each command. The command is sent by simply appending it to the command file that the server is reading. I append an extra character to the end of each command so that I can send an empty string. The server batch script strips off the trailing character before piping it to the actual server.
I could have used two separate batch scripts, one for the controller, and one for the server. But I opted to put everything in a single script. The demo script should be called without any parameters. The script STARTs a new server window that calls itself, but this time with a :Server parameter. The :Server parameter effectively instructs the script to branch to the server code.
When you run the demo script, it will open a new server window. The controller window enters a loop that waits 5 seconds and then reads the next command and sends it. The server window will read and execute each command that is sent. The server window will automatically close once it receives the EXIT command.
Code: Select all
@echo off
if "%~1" neq "" call %1& exit /b
setlocal disableDelayedExpansion
:: Configure the server
set "server=cmd.exe" Set to the name (or full path) of your server executable
set "serverTitle=My Server" Set to the title for your server window
set "quitCmd=EXIT" Set to the command that kills your server
set "cmdFile=%temp%\myServerCommands.txt" Define a file where server commands should be written
set "delay=5" Set to the number of seconds delay between each command
:: Create an empty command file
copy /y nul "%cmdFile%" >nul
call :LaunchServer
:: Command List, with each command preceded by :::
:::echo Hello world!
:::dir
:::pause
:::
:::echo Goodbye
:::exit
:: Controller
for /f "delims=: tokens=*" %%C in ('findstr "^:::" "%~f0"') do (
timeout %delay%
echo Sending: %%C
(echo(%%C#) >>"%cmdFile%"
)
exit /b
:Server
setlocal enableDelayedExpansion
call :ServerLoop <"%cmdFile%"
del "%cmdFile%"
exit
:ServerLoop
set "cmd="
set /p "cmd="
if defined cmd (
(echo(!cmd:~0,-1!)
if /i "!cmd:~0,-1!" == "!quitCmd!" exit /b
) else (
>nul ping localhost /n 2
)
goto :ServerLoop
:LaunchServer
start "%serverTitle%" cmd /c ^""%~f0" :Server ^| "%server%""
exit /b
Dave Benham