Determining if batch script has been started/executed from the command line (cmd) -or- To pause or not to pause? - windows

I like to have a typical "usage:" line in my cmd.exe scripts — if a parameter is missing, user is given simple reminder of how the script is to be used.
The problem is that I can't safely predict whether potential user would use GUI or CLI. If somebody using GUI double-clicks this script in Explorer window, they won't have chance to read anything, unless I pause the window. If they use CLI, pause will bother them.
So I'm looking for a way to detect it.
#echo off
if _%1_==__ (
echo usage: %nx0: filename
rem now pause or not to pause?
)
Is there a nice solution on this?

You can check the value of %CMDCMDLINE% variable. It contains the command that was used to launch cmd.exe.
I prepared a test .bat file:
#Echo Off
echo %CMDCMDLINE%
pause
When run from inside of open cmd.exe window, the script prints "C:\Windows\system32\cmd.exe".
When run by double-clicking, it prints cmd /c ""C:\Users\mbu\Desktop\test.bat" "
So to check if your script was launched by double-clicking you need to check if %cmdcmdline% contains the path to your script. The final solution would look like this:
#echo off
set interactive=1
echo %cmdcmdline% | find /i "%~0" >nul
if not errorlevel 1 set interactive=0
rem now I can use %interactive% anywhere
if _%1_==__ (
echo usage: %~nx0 filename
if _%interactive%_==_0_ pause
)
Edit: implemented fixes for issues changes discussed in comments; edited example to demonstrate them

:: exit if not interactive
echo %CMDCMDLINE% | find /i "/c"
if not ERRORLEVEL 1 goto:eof

Here, I wrote something...
Usage.bat
#echo off
if arg%1==arg goto help
goto action
:action
echo do something...
goto end
:help
set help1=This is Help line 1.
set help2=This is Help line 2.
cmd.exe /k "echo %help1% &echo %help2%"
goto end
:end
It's not perfect, but it works! :D
-joedf

This is only using the internal command. so effectively....
EnableDelayedExpansion
if "!cmdcmdline!" neq "!cmdcmdline:%~f0=!" pause >nul
or
if not "!cmdcmdline!" == "!cmdcmdline:%~f0=!" pause >nul
DisableDelayedExpansion
if "%cmdcmdline%" neq "%cmdcmdline:%~f0=%" pause >nul
or
if not "%cmdcmdline%" == "%cmdcmdline:%~f0=%" pause >nul

Start your batch checking for %WINDIR% in %cmdcmdline% like this:
echo "%cmdcmdline%" | findstr /ic:"%windir%" >nul && (
echo Interactive run of: %0 is not allowed
pause
exit /B 1
)

Please use findstr
echo %cmdcmdline% | findstr /ic:"%~f0" >nul && ( pause >nul )
or
setlocal EnableDelayedExpansion
.
.
echo !cmdcmdline! | findstr /ic:"%~f0" >nul && ( pause >nul )
.
.
endlocal
This is always worked...

for internal command
setlocal EnableDelayedExpansion
set "cmddiff=!cmdcmdline:~0,1!" & if !cmddiff! neq ^" ( pause >nul )
endlocal
or
setlocal EnableDelayedExpansion
set "cmddiff=!cmdcmdline:~28,1!" & if !cmddiff! neq ^" ( pause >nul )
endlocal
You can compare the different thing, but this is only worked within EnableDelayedExpansion. and I don't think that this will be always worked, cause windows version, etc...

Similar approach...
setlocal
set startedFromExplorer=
echo %cmdcmdline% | find /i "cmd.exe /c """"%~0""" >nul
if not errorlevel 1 set startedFromExplorer=1
...
if defined startedFromExplorer pause
goto :EOF

setlocal EnableDelayedExpansion
if "!cmdcmdline!" neq "!cmdcmdline:%comspec%=!" ( pause>nul )
Test is done in Windows 10. Using %windir%, it is a little dangerous or ambiguous. So %comspec% is super safe.

Related

Batch: How to scroll through files to read to a variable?

I'm trying to have it scroll through a directory and present a new variable when the user replies "N". I have it all figured out except how to go to the next variable.
cd "C:\Test"
for /r %%F in (*) do SET Show=%%~NF
echo %Show%
echo.
SET /P Continue=Continue?(Y/N)
if /I "%Continue%" EQU "y" goto :Run
if /I "%Continue%" EQU "n" goto :Start
If you're looking to scroll the directory and prompt the user the file name and have them choose to choose it or continue, then bellow should help you.
Firstly, we can use dir /b /a:d to display only directories (folders) in the the current directory. By using a code block ( & ) we can put batch script inside the for loop. For your sake, we can use the CHOICE command to prompt to continue the loop or to save current folder to string and do something with it.
ScrollTreeWithPrompt.bat:
#echo off
setlocal EnableDelayedExpansion
Rem | Configuration
Set "MainDir=C:\Test"
Rem | Get Each Project Folder
for /f "tokens=*" %%A in ('dir "!MainDir!\" /b /a:d') do (
Cls
Echo Current Folder: %%A
echo(
CHOICE /M "Continue?"
Rem | Check for "N" - If so Set String & goto
IF "!ERRORLEVEL!"=="2" (
Set "Choice=%%A"
GOTO Run
)
)
Rem | No Further Results
Cls
Echo Warning: No further folders found.
pause>NUL
goto :EOF
:Run
Cls
echo Currently selected: !MainDir!\!Choice!
pause>NUL
goto :EOF
I have left a few Rem comments in the script to help you along. For any more help on the commands, type the following into a command prompt:
choice /?
set /?
for /?
goto /?
Is this what you need:
For /R "C:\Test" %%A In (*) Do (Choice /M "%%~nA"
If Not ErrorLevel 2 (Set "Show=%%~nA" & GoTo Run))
Exit /B
:Run
Echo Running against %Show%
Pause
Alternatively, should you wish to return to the loop after running against the file name, then use Call instead of GoTo:
For /R "C:\Test" %%A In (*) Do (Choice /M "%%~nA"
If Not ErrorLevel 2 Call :Run "%%~nA")
Exit /B
:Run
Set "Show=%~1"
Echo Running against %Show%
Pause

using for loop and windows tasklist cmd i am trying to open several programs from a batch file which are not already running

My code below doesn't work
echo will now go ahead and load the essential programs needed for
project: %project%
set "programlistopen=sublime_text.exe xampp-control.exe"
for /f "delims=" %%j in (%programlistopen%) do (
if %%j=="sublime_text"(set pathtopen=%defaultpath%\Sublime_Text&&set
dirpath="%workdirectory%\%project%") else
if %%j=="xampp-control.exe"(set pathtopen=%xampp%\&&set dirpath=)
tasklist /FI "IMAGENAME eq %%j" 2>NUL | find /I /N "%%j">NUL
if "%ERRORLEVEL%"=="1" goto notstartedsublimetext
if "%ERRORLEVEL%"=="0" goto startedsublimetext
:notstartedsublimetext
Echo started %%j at %time%
pause
start /d "%pathtopen%" %%j %dirpath%
:startedsublimetext
echo %%j is already running......
)
I would suggest possibly changing the structure to this:
#Echo Off
For %%A In ("C:\Program Files\SomeName\madeup.exe"
"C:\Users\Vibrations\Portables\Some Name\dummy.exe"
"C:\Windows\built-in.exe"
) Do TaskList|FindStr/BRIC:"%%~nxA\>">Nul&&(Echo %%~nxA already running
)||(Start "" "%%~A"&Echo Started %%~nxA at %TIME%)
Pause
Just list the full paths to the executables you want to check/start similarly to the three I've used as an example
Edit
As a result of the latest comment, here is an example of what I assume you're trying to do by way of your modification, (replace from line 6):
)||(If "%%~nxA"=="sublime_text.exe" (Start "" "%%~A" "%workdirectory%\%project%"
) Else Start "" "%%~A"
Echo Started %%~nxA at %TIME%)
Pause
#echo off
setlocal
#rem Variables to be set.
#rem changedir is an optional pushd on each call of :notstarted.
#rem startdir is passed to the /d argument in the start command.
#rem programpath is the path to the program executable.
#rem args are any arguments to pass to the program executable.
#rem Variables are undefined at start of loop.
echo will now go ahead and load the essential programs needed for project: %project%
set "programlistopen=wordpad.exe notepad.exe"
for %%j in (%programlistopen%) do (
for %%B in (changedir startdir programpath args) do set "%%~B="
if "%%~j" == "wordpad.exe" (
set "changedir=%userprofile%"
set "startdir=%cd%"
set "programpath=C:\Program Files\Windows NT\Accessories"
) else if "%%~j" == "notepad.exe" (
rem Nothing to set. Just start notepad.exe.
)
tasklist /FI "IMAGENAME eq %%~j" 2>NUL | find /I /N "%%~j" >NUL
if errorlevel 1 (
call :notstarted "%%~j"
) else call :started "%%~j"
)
exit /b
:notstarted
setlocal
echo started "%~1" at %time%
set "cli=start """
if defined startdir set "cli=%cli% /d "%startdir%""
if defined programpath (
set "cli=%cli% "%programpath%\%~1""
) else set "cli=%cli% "%~1""
if defined args set "cli=%cli% %args%"
if defined changedir pushd "%changedir%"
echo %cli%
%cli%
if defined changedir popd
exit /b
:started
echo "%%~1" is already running......
exit /b
Your code has many unknowns so I have done an example which you
can adapt to your needs.
I chose some simple programs like notepad and wordpad to test with.
Variables for use are commented at the top of the script. These
variables are undefined at start of each loop so you do not need
to undefined them and just use the variables that you want to set
with each program.
The errorlevel of tasklist is checked and will call the name of
the label required. This allows the call to return back to the
position of the caller so the loop can continue.
The :notstarted label will bulid the command line (cli) with
each of the defined variables that are relevent to the cli. The
optional pushd and popd was added just for an unusual use.

Running two command prompt terminals in native windows, how can I redirect the output of one to the input of the other?

I have one Windows 10 command prompt running and awaiting input, and I wish to automate continuous and live input with a second command prompt. I have gotten the second command prompt to extract the desired variable, and I wish to send it to the other command prompt that is waiting for input.
The "awaiting input" command prompt must run in real time because it is connected to Plink (not an SSH session so no use of the -m command here) which is connecting to a microcontroller. So it cannot be accomplished (at least I don't think) with function calls.
I see that it can be done in UNIX environments: https://askubuntu.com/questions/496914/write-command-in-one-terminal-see-result-on-other-one
Thanks in advance and please advise,
--A hopeful beginner
Batch code starts 2 piped processes, one for getting keyboard input and writing to a file, and the other reading the data written. Note there isn't a cmd window for each process, but there are two new processes running. You may use cmd /c or start if you need two consoles.
Does it help?
#echo off
set "pipefile=pipefile.txt"
if "%~1" neq "" goto %1
copy nul "%pipefile%" >nul
"%~F0" getInput >>"%pipefile%" | "%~F0" readInput <"%pipefile%"
echo(
echo(Batch end...
ping localhost -n 1 >nul
del /F /Q "%pipefile%" 2>nul
exit/B
:getInput
set "input="
set/P "input="<CON
echo(%input%
if /I "%input%" equ "EXIT" exit
goto:getInput
:readInput
setlocal enableDelayedExpansion
set/P="enter some data [EXIT to exit] "
for /l %%. in () do (
set "input="
set/P "input="
if defined input (
if /I "!input!" equ "EXIT" exit
set/P="enter some data [EXIT to exit] "
)
ping 1.1.1.1 -w 10 -n 1 >nul 2>nul & rem avoid processor load (may be removed)
)
Running two console windows
#echo off
set "pipefile=pipefile.txt"
if "%~1" neq "" goto %1
del /F /Q "%pipefile%" 2>nul
copy nul "%pipefile%" >nul
start "writer" cmd /c ^""%~f0" readInput ^<"%pipefile%" ^"
start "reader" cmd /c ^""%~f0" getInput ^"
echo(
Echo batch end...
ping localhost -n 1 >nul
goto :EOF
:getInput
set "input="
set/P "input=enter some data [EXIT to exit] "
echo(%input%>>"%pipefile%"
if /I "%input%" equ "EXIT" exit
goto:getInput
:readInput
setlocal enableDelayedExpansion
for /l %%. in () do (
set "input="
set /p "input="
if defined input (
if /I "!input!" equ "EXIT" exit
echo(!input!
)
ping 1.1.1.1 -w 10 -n 1 >nul 2>nul & rem avoid processor load (may be removed)
)

Batch file does action after it's closed

I want to create a batch file, that detects when the CMD window gets closed and does directly after an action.
If for example the the textfile text.txt is opened, and after i launch the batch file called prog.bat with this code inside:
#echo off
echo Hello
pause
How can i tell the batch that if the CMD window which is currently opend, should taskill text.txt file when somebody closes the CMD window?(by closing with ending process or hitting the X on the top)
This is the accepted answer at this question:
#echo off
if "%1" equ "Restarted" goto %1
start "" /WAIT /B "%~F0" Restarted
echo Execute here anything you want when the Batch file is closed...
goto :EOF
:Restarted
echo Hello
pause
exit
Im not one of the best but you could do this.
You need to create 3 files.
Start.bat
#echo off
TITLE Start.bat
REM :: THIS FILE OPEN'S THE CHECK WINDOW BATCH ::
start Init2.bat
ping localhost -n 1 >nul
REM :: IF I WOULDN'T HAVE THE "PING" THE CHECK WINDOW BATCH WOULD BE ON TOP ::
start Init1.bat
Init1.bat
#echo off
REM :: YOU CAN WRITE WHAT YOU WANT HERE ::
REM :: YOUST REMEMBER TO CHANGE "Init2.bat" WHEN YOU CHANGE THE TITLE ::
TITLE Init1.bat
echo.HELLO
pause>nul
Init2.bat
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
TITLE Init2.bat
:Start
REM :: GET PID ::
set "PID="
for /F "skip=3 delims=" %%A in ('TASKLIST /FI "WINDOWTITLE eq Init1.bat"') do (
set "A=%%A"
set "A=!A:~26,8!"
set "A=!A: =!"
set "PID=!A!"
set "A="
echo.!PID!
goto Test
)
REM :: IF NO WINDOWS NAMED "Init1.bat" exit ::
if not defined "PID" (
echo.No Window!
goto Exit
)
:Test
set "true=0"
for /F "skip=3 delims=" %%A in ('TASKLIST /FI "PID eq !PID!"') do (
set "true=1"
)
REM :: IF WINDOW CLOSED ::
if "!true!" EQU "0" (
echo.No Window!
goto Exit
)
goto Test
:Exit
REM :: HERE ARE YOU WRITING THE CLOSING FILE ::
ren testtxt.txt prog.bat
echo.#echo off > prog.bat
echo.echo hello >> prog.bat
echo.pause >> prog.bat
exit
Hope this help.
This is not a wiki answer, but i hope you find it helpful.
The only thing is that you have a window behind the first, but it works.

Findstr batch file in finding listener down servers

Hi I'm trying to create a batch file to filter out servers which has RDP/ICA listener down from a list of servers in a notepad file, I created this script with the below syntax, but for some reasons it won't work as expected, can some one help me fix the situation?
I've a list of servers in computer.txt file and I'm trying to find the one's which are down and if errorlevel is 0, meaning the string down is found, I want the server name to be printed in listenerdown.txt , but for some reasons, if I execute the batch file, all the servers in computer.txt gets written to listenerdown.txt file
below is the batch file
for /f %%i in (computer.txt) do(
qwinsta /server:%%i | findstr/i down >nul 2>&1
if %errorlevel% neq 1
echo %%i >>Listenerdown.txt
)
Move the echo onto the same line as the if statement or else use parentheses to establish scope and %ErrorLevel% will always be 0 because the variable does not get set in a loop without delayed expansion.
setlocal EnableDelayedExpansion
for /f %%i in (computer.txt) do(
qwinsta /server:%%i | findstr /i down >nul 2>&1
if !errorlevel! neq 1 echo %%i>>Listenerdown.txt
)
endlocal
or
setlocal EnableDelayedExpansion
for /f %%i in (computer.txt) do(
qwinsta /server:%%i | findstr /i down >nul 2>&1
if !errorlevel! neq 1 (
echo %%i>>Listenerdown.txt
)
)
endlocal
Cmd.exe parses batch files line by line and unless you tell it that the scope of the command continues onto the next line it will think the command is finished.
You should use setlocal EnableDelayedExpansion, and !errorlevel! instead of %errorlevel%:
#echo off
setlocal EnableDelayedExpansion
for /f %%i in (computer.txt) do (
qwinsta /server:%%i | findstr/i down >nul 2>&1
if !errorlevel! neq 1 echo %%i >>Listenerdown.txt
)
otherwise the value of errorlevel would be expanded only once, before entering the loop, and would not have the correct value. You also have to make sure that echo is in the same row as the if.

Resources