I'm creating a simple command line using Batch for a personal project. However, whenever I try to execute a batch file in it, the command line closes as soon as the batch file completes. Why is that, and how can I fix it?
This is the relevant bit of source (also an SSCCE):
#echo off
:loopstart
set /p comnd=%cd%^>
%comnd%
goto loopstart
I have comments but must give you an answer, so couple things:
The /p option in Set generates a prompt for user input, so it's waiting for an answer but you are not handling any user response.
You have set up an infinite loop with the goto at the end (but that doesn't necessarily cause CMD window to close).
Rem out the goto at end and add a pause and you should be able to track down the problem.
EDIT: New answer per user's comments ---------------------------------------
Use call in this bat and exit /b at end of each bat you're running from this prompt.
:loopstart
set /p comnd=%cd%^>
call %comnd%
goto loopstart
Related
I am coding a batch file and it needs some more files. But they files should only be able to run using the call function from another batch file. My code looks like this:
call compileData.bat
pause
I want the compilerData.bat just starts when it's called from this one, not if its just started from Explorer or something other.
Can you please help me?
I have tried to find a solution on this problem in a whole hour!
You can use a parameter.
compileData.bat:
if "%1" neq "somestring" exit /b
REM rest of your code
Another.bat:
call compileData.bat somestring
pause
I cannot think of any way that would prevent the bare "run" of the called script. Possibly that might only be done using NTFS permissions.
What you can do quickly is something like this:
MOTHERBATCH.bat
call compileData.bat SomePASSPHRASE
compileData.bat
#echo off
if not "%1"=="SomePASSPHRASE" (
echo "You can not run this script directly, please run MOTHERSCRIPT.bat."
exit /B 1
)
echo "Passphrase is correct, code is executed..."
Set an environment variable in the parent script, then if that variable is not set or doesn't have the correct value in the children, they just exit with an error message explaining they aren't intended for standalone use. You really can't prevent someone from reverse engineering the code and forcing it to run.
You could put the children in a password protected zip file and have the parent unpack it just before calling them. Then when the parent is done, it deletes the unpacked scripts.
Do all of the above.
You can use a not so well known system variable named cmdcmdline.
I will explain a brief usage for you.
For brevity's sake we will have two very simple batch files.
Parent.bat
#echo off
call compiledata.bat
And compiledata.bat
#echo off
echo %cmdcmdline%
pause
When compiledata.bat is executed on its own this variable's value is the batch file itself.
C:\WINDOWS\system32\cmd.exe /c ""C:\Batch\CALL\compiledata.bat" "
But when compiledata.bat is called from parent.bat the variable's value is that of the calling parent.bat.
C:\WINDOWS\system32\cmd.exe /c ""C:\Batch\CALL\parent.bat" "
My suggestion is putting all your batch code into a single batch file and use subroutines. Open a command prompt window and run call /? for help on how to use subroutines which is nothing else than calling a batch file being embedded in current batch file.
A simple example:
#echo off
echo Running %~f0 %*
call :compileData %*
call :WaitForUser
rem The next line results in exiting processing of this batch file
goto :EOF
:compileData
echo/
echo Running subroutine compileData with the arguments: %*
rem Exit processing subroutine compileData and continue above
rem after the command line calling the subroutine compileData.
goto :EOF
:WaitForUser
echo/
pause
rem Exit processing subroutine WaitForUser and continue above
rem after the command line calling the subroutine WaitForUser.
goto :EOF
See also Where does GOTO :EOF return to? And take a look on DosTips forum topic ECHO. FAILS to give text or blank line - Instead use ECHO/ for the explanation on using echo/ to output an empty line.
Here's my solution:
when launched from the command line, %cmdcmdline% inherits the name from the base calling program, so it wouldn't be the name of the "middle man" calling your batch file
this is what I came up with. I had to use the "subroutine" method to get the variables properly expanded
Note: Edge Case: if you use complex paths with the batch files having the same name in different folders, you could run into an "Edge Case". If that is important to you, then you might have to further parse the file names. I'm not totally sure, it wasn't my use case so I didn't go further.
#echo OFF
setlocal EnableDelayedExpansion
call :myGetFileName "%CmdCmdLine%"
if /I "%sRet%"=="%~nx0" (
echo ************** Pause
) else (
echo ************** NO Pause
)
echo finished test
pause
exit
:myGetFileName
set "sRet=%~nx1"
exit /b
As a small training for my batch programming I originally followed the idea to make up a small slotmachine. You can give your coins, the numbers rush through and when you hit a button, the numbers should stop at their current value. If they are all the same, boom, you won the jackpot!
My problem right now is the keystroke capture during the loop. I already thought something like a choice command, but then the program would stop at each loop waiting for keyboard input, not making the game just quite annoying to wait all the time, but as well boring as you could check if you want to hit a specific button to stop.
Another thought was putting
set /p foobar=
and then simulate an Enter-Stroke with !SendKeys! (with everything neccessary in the code), forgetting that the enter yould have been sended after input...
Is there a way to accomplish that in ONE batch file? Or do I have to come up with another one to simulate the keystrokes or is there anything else I have missed?
EDIT: To clearify: Is there any command that changes something on keystroke, but runs through if nothing is touched?
Thanks for help in advance!
Greetings
geisterfurz007
#echo off
setlocal EnableDelayedExpansion
rem Create an empty file
cd . > key.txt
rem Start a parallel process that wait for Enter key
rem and add a line to the empty file
start "" /B cmd /C "set /P = & echo line >> key.txt"
set "key="
:wait
cls
set "number=%random:~-4%"
echo %number%
echo/
echo Press Enter key to stop the numbers...
set /P key=< key.txt
if not defined key goto wait
echo The last number is: %number%
Yes, I know you are probably going to complain saying it's a bad thing to do, but I want to do it anyway!
I am creating a batch program and at the end, I need it to hang and not accept user input. I know one method is just creating an infinite loop of:
:pause
pause > nul
goto pause
but I don't think that's a great choice. Although I need it to hang, I need to to be able to be closed via the red 'X' close button at the top of the window.
Any ideas?
This works for me. It redirects < NUL into self to prevent Ctrl+C from breaking, and uses start /b /wait to suppress the "Terminate batch job (Y/N)?" prompts.
#echo off
setlocal
>NUL (echo(%* | findstr "\<hang\>" && waitfor redX)
rem // *** PUT YOUR MAIN SCRIPT HERE ***
echo End of line.
rem // ******* END MAIN SCRIPT *********
call :hang
goto :EOF
:hang
start /b /wait "" "%~f0" hang ^<NUL
On the initial launch of the script, the echo(%* | findstr "\<hang\>" >NUL line looks for a script argument of "hang". If found, the script executes the waitfor command.
Normally, waitfor can be broken with Ctrl+C. But since the usual behavior of Ctrl+C is defeated by start /b and <NUL, the hanging effect is achieved unless a user does Ctrl+Break or sends the answering waitfor signal.
The red X still works, though.
I'm really new in this forum so I hope to respect all your rules, if not please forgive me!
I've just started studying something about batch files and I'm trying to execute a simple program from batch passing a parameter (the last aim is to submit a SAS program passing a date parameter).
Is it possible to activate a sort of list where I can choose some between pre-defined parameters?
--> This is the real aim of my work
I'm trying to "play" with this code:
#echo off
title Setting up execution period
echo Insert your date in the format GGMMMAAAA (es: '31DEC2003'D).
SET /p data_par=Insert the date to filter datas:
SET first_byte=%data_par:~1,1%
if "%first_byte%"=="" (
GOTO tag1
) else (
GOTO tag2
)
:tag1
msg * Missing value
:tag2
msg * Well done!
pause
I've tried in a lot of ways but it looks like the IF statement is not executed, I don't know where am I wrong.
Another question: why the prompt closes after i press "Enter" (afte the set/p command is executed)?
--> this has been resolved putting the "pause" command at the end of the script.
Thank you all for the attention,
Best regards!
Squotty
Put a pause at the end of your code to see the errormessages.
correct syntax for if when using else is:
if "a"=="b" (dosomething) else (dosemethingelse)
You can write it in several lines, but there are rules, where to set the paratheses:
if "a"=="b" (
echo this is code for something
rem more lines possible
) else (
echo this is code for something else
rem more lines possible
)
The first ( has to be on the same line than if.
) else ( have to be on one line.
If you press just enter with set /p, the variable remains unchanged (propably empty), so your code will go on with the code and hits the line else. Here it will tell you "else is not recognized as a command..."
at your tagx you should tell batch, where to stop execution. Use goto :eof to stop execution or goto somewhere to continue somewhere else. If you don't, it will just continue with the next lines.
Example:
:tag1
msg * Missing value
goto :eof
:tag2
msg * Well done!
goto :continue
pause
:continue
REM go on with the program...
(note: the pause will never be reached. I let it there to show you, how things work)
EDIT instead of just checking for some input you can check for the correct format:
echo %data_par%|findstr /r "[0-3][0-9][A-Z][A-Z][A-Z][1-2]0[0-9][0-9]">nul && (
echo correct format
goto continue
) || (
echo wrong format
goto startover
)
It's not bullet proof (eg. 38ABC2019 would be considered "correct"), but at least it checks for the correct format (e.g. 15.12.2019 or 12/15/2014 would be "not correct")
#ECHO OFF
SETLOCAL
SET "item1=date1"
SET "item2=date2"
SET "item3=date3"
SET "item4=date4"
FOR /l %%a IN (1,1,4) DO CALL ECHO(%%a. %%item%%a%%
ECHO(U. User-specified
choice /c 1234u
CALL SET selection=%%item%errorlevel%%%
IF NOT DEFINED selection (
SET /p selection="Your date-selection ? "
)
IF NOT DEFINED selection ECHO No selection made&GOTO :EOF
ECHO selection is %selection%
GOTO :EOF
This code may be of assistance.
It's normal to develop batch code using a batch window. Simply set up a shortcut to command prompt (Start>Programs>Accessories) which would allow you to run the script over and over and retain the results on-screen without using 'pause'. Editing can be accomplished by using notepad batchfilename.bat from the prompt (if you are using notepad for an editor - if using something better, then substitute that program's name). You can exit from the batch window by executing an exit command.
You can also get help on batch commands by using commandname /? - it's often cryptic and there are plenty of quirks. Extensive help available here on SO.
I like to have a final PAUSE in my *.bat scripts so I can just double click on them in Windows explorer and have the chance to read the output. However, the final PAUSE is an annoyance when I run the same script from the command line.
Is there any way to detect whether we are running the script from a command prompt (or not) and insert the PAUSE (or not) accordingly?
(Target environment is Windows XP and greater.)
Update
I've managed to compose this from Anders's answer:
(((echo.%cmdcmdline%)|find /I "%~0")>nul)
if %errorlevel% equ 0 (
set GUI=1
) else (
set CLI=1
)
Then, I can do stuff like this:
if defined GUI pause
#echo off
echo.Hello World
(((echo.%cmdcmdline%)|find /I "%~0")>nul)&&pause
...NT+ only, no %cmdcmdline% in Win9x probably.
As pointed out by E M in the comments, putting all of this on one line opens you up to some edge cases where %cmdcmdline% will escape out of the parenthesis. The workaround is to use two lines:
#echo off
echo.Hello World
echo.%cmdcmdline% | find /I "%~0" >nul
if not errorlevel 1 pause
I doubt that there's a distinction, because I think it just starts a command prompt and then runs the bat when you double click on it.
However, if you make shortcuts to the bat files and go to Properties and add in an extra argument (something like "/p") in the "Target" field, then you could check for the presence of that argument at the end of the script and pause if it is set. Then, running from the shortcut would cause it to end in a pause and running from command line wouldn't.
I was hoping the answer by #Anders would work in its own .bat file. Unfortunately, it does not for me. Based on #DarinH's comment, perhaps it does for some. The script below should work for all, but requires an extra parameter.
The key lies in the %CmdCmdLine% environment variable, which I imagine might be a bit different for a few edge cases.
PauseIfGui.bat
#echo off
if "%~1" == "" ((echo.%CmdCmdLine%)|"%WinDir%\System32\find.exe" /I "%~0")>nul && pause & exit /b
((echo.%CmdCmdLine%)|"%WinDir%\System32\find.exe" /I "%~1")>nul && pause
This accepts one optional parameter: the full path of calling script. If no params are passed, it runs the same as #Anders script.
AnyOtherFile.bat
#echo off
call PauseIfGui.bat %~f0
If opened from Explorer (i.e. double-clicking) , AnyOtherFile.bat will pause. If called from a command prompt, it will not.