I tried to customize windows command prompt with the following batch file.
#echo off
cls
:cmd
set /p "cmd=%cd%>"
%cmd%
goto cmd
So, when I open the batch file, it just takes my command into cmd variable and executes it and again prompts for a new command.
But the following command echo %cd% outputs only %cd%
Then I enabled delayedexpansion and used echo !cd! and got the desired output.
I think, because of the delayed expansion, cmd variable now holds echo c:\Users\Sourav\Desktop (am I correct?)
But I got confused when I tried to open the command prompt (not the batch file) and tried the following commands.
I thought, I will get c:\Users\Sourav\Desktop but I got !cd!. This contradicts my understanding of how echo !cd! is working in first case.
Why am I getting different output in the second case?
Can anyone suggest any improvement to the batch file, so that I can get desired output just using echo %cd% in first case?
you need another level of parsing. You can use call to do so:
#echo off
cls
:cmd
set /p "cmd=%cd%>>"
call %cmd%
goto cmd
Related
I'm new to Windows cmd and .bat, and to Tesseract. But thanks to this list I've managed a couple of successes.
My first success was this cmd-window line:
tesseract.exe -l eng+lat+ita D:\TIFs\Convivio.tiff D:\TIFs\Convivio
My next success was the .bat file:
:Start
#Echo off
ECHO.
ECHO This is a batch file
ECHO.
PAUSE
BREAK=ON
Set _SourcePath=D:\temp\TIFs\*.tif
Set _OutputPath=D:\temp\TIFs\
Set _Tesseract="D:\temp\Tesseract-OCR\tesseract.exe"
:Convert
For %%A in (%_SourcePath%) Do Echo Converting "%%A"...... &"D:\temp\Tesseract-OCR\tesseract.exe" "%%A" "%_OutputPath%%%~nA"
PAUSE
:End
Set "_SourcePath="
Set "_OutputPath="
Set "_Tesseract="
The problem now is how to include in the .bat file that"-l eng+lat+ita" bit from the cmd-window line.
I got the idea that this is possible from an explanation of the "For" command, which states that "do command" can be followed by "CommandLineOptions" (i.e., "-l eng+lat+ita").
Any help would be much appreciated... 'cause I've been banging my head on this one for hours now...
UPDATE: Found an alternative, but still would like an answer to my question.
I didn't know that "FOR" commands could be run from cmd. So, I pasted the folllowing line in the cmd window:
for %i in (*.tif) do "D:\temp\Tesseract-OCR\tesseract.exe" -l eng+lat+ita "%i" "D:\temp\%~ni"
And, it worked!
As I say, though, how to do this with a .bat file?
#ECHO OFF
SETLOCAL
:Start
#Echo off
ECHO.
ECHO This is a batch file
ECHO.
PAUSE
BREAK=ON
Set "_SourcePath=D:\temp\TIFs\*.tif"
Set "_OutputPath=D:\temp\TIFs"
Set "_Tesseract=D:\temp\Tesseract-OCR\tesseract.exe"
:Convert
For %%A in ("%_SourcePath%") Do Echo Converting "%%A"...... &"%_Tesseract%" -l eng+lat+ita "%%A" "%_OutputPath%\%%~nA"
PAUSE
:End
rem Set "_SourcePath="
rem Set "_OutputPath="
rem Set "_Tesseract="
GOTO :EOF
Since I don't have the tesseract utility, I used another. The above worked for me as I expected with that other utility, so no guarantees with tesseract.
It's normal practice to start a batch with setlocal which makes the clean-up effort unnecessary (hence remmed-out) since an implicit endlocal is executed when the batch terminates, restoring the environment to its initial state.
Assigning values containing quotes is valid but awkward when combining elements. Ditto terminating a value with a backslash. I've converted your code to my preferred syntax. Note that the syntax SET "var=value" (where value may be empty) is used to ensure that any stray trailing spaces are NOT included in the value assigned.
Will it work in your situation? Over to you to try.
Here is my problem (which is more a workflow concern than a real problem):
I'm currently learning how to use Symfony 4 and as you may know, this framework uses a lot of commands with PHP, using the $ php bin/console command command line.
Here's my question: is there any possible way to configure a CMD window so I dont have to always type the $ php bin/console before the command name?
You will need a batch file to accomplish your task
Save the following code in your project folder where the bin folder and other Symfony files resides and name it SymfonyConsole.bat
#echo off
:SymfonyConsolePrompt
set "SymfonyConsoleCommand="
echo,
set /p "SymfonyConsoleCommand=Symfony Console Command>"
for /F "tokens=*" %%C in ("%SymfonyConsoleCommand%") do set "SymfonyConsoleCommand=%%C"
if defined SymfonyConsoleCommand (
php bin/console %SymfonyConsoleCommand%
) else (
choice /C NY /N /M "Do you want to quit Symfony Console Prompt?[YN]"
if errorlevel 2 exit /b
)
goto :SymfonyConsolePrompt
Then at the command prompt type > SymfonyConsole
You will get this prompt Symfony Console Command> ready to get commands for the console component of Sympony.
ex: Symfony Console Command>server:start 0.0.0.0:8000 will automatically run php console/bin server:start 0.0.0.0:8000 and then waits for the next Sympony console command until you hit enter without entering anything in which case you can quit the batch file and return to the cmd prompt.
The easiest (and recommended) way would be to load the php bin/console into a variable then just reuse the variable
I'm not familiar with the $ php bin/console syntax but I think in windows script the equivalent is just php bin/console
So you can try this:
SET phpconsole=php bin/console/
"%phpconsole%Command1"
"%phpconsole%Command2"
would result in this:
"php bin/console/Command1"
"php bin/console/Command2"
What does your existing windows batch file look like?
#echo off
setlocal
:: Check 1st argument for help.
if "%~1" == "-h" goto :help
if "%~1" == "--help" goto :help
if "%~1" == "/?" goto :help
:: Check script arguments for program.
set _program=%*
:: If not program defined, prompt for program.
if not defined _program set /p "_program=Enter the program: "
:: If the required program is not defined then exit.
if not defined _program exit /b 1
:: Set a prompt string.
set "_prompt=%_program:"='%>"
:: Show information before start of the loop.
echo Pass arguments to '%_program%' in a loop.
echo.
:begin_loop
:: Prompt for arguments.
set "_args="
set /p "_args=%_prompt% "
:: Restart loop if no args or exit if requested.
if not defined _args goto :begin_loop
if /i "%_args:"='%" == "exit" exit /b 0
:: Clear local variables and then run the program with arguments.
setlocal
set "_prompt=" & set "_program=" & set "_args=" & %_program% %_args%
endlocal
echo.
goto :begin_loop
:help
:: Help information.
echo Pass arguments to a select program in a loop.
echo If script arguments are passed, then that will be the program.
echo If a program is not defined, then a prompt will ask for a program.
echo Variables _prompt, _program and _args are cleared with each execution.
This is same as I have posted at my website, except a line
removed that sets a ANSI escaped prompt for conemu. Doubt this
site would keep the control character intact if posted.
I call it loop.cmd and place it in PATH (see note). It is made for any
command so is universal.
To use:
loop command
then you will get a prompt with the command in it i.e.
command>
What you type at the prompt now will be prefixed with
command and executed.
You can just type exit to end the loop.
Based on your code example of use:
loop php bin/console
then add your command at the prompt:
php bin/console> command
A Python version exists too though I use the batch file
as it keeps auto completion etc.
Note:
The PATH I mention is where the script can find files on your system.
A path in the PATH environmental variable or the current
working directory.
Recommend creating a folder path i.e. %SystemDrive%\bin and add
that to the system variable PATH where your PATH suitable
scripts can be stored. Or you could just add the script to an
existing directory in the variable PATH if you are uncomfortable
to add your own directory to PATH.
How to set the path and environment variables in Windows
This is an update for an old question of mine, because I wasn't so clear in it.
Honestly I didn't even have the Win_CLI experience to ask the right question. So I'm sorry, and I wish this time is better.
Like I said in my question, I need to use a command output as an argument of another command.
For Example:-
CLITool -Switch (Value_OF_a_Command_OutPut)
I think what I need here is the Set command:-
Set Value=
Command
CLITool -Switch %Value%
But, the problem is that the output value will be printed with a Line_Break!
OutPut_Value
CLITool -Switch (Nothing!)
and because of this, the command won't be executed correctly.
So.. my question now, is how to fix that Set Command problem,
and If possible, how to do the same thing in another technique?
BTW..As most of Linux users know, it's possible to use a command output in the same commandline of the other command as follow:
CLITool -Switch $(Command)
I wish if there is something similar in windows CLI.
Please tell me if it's better to post my actual command.
Thanks Guys,
Appreciate Your Help :)
You can use a for /f the command. For each line in the output of the executed command, the code in the do clause will be executed, with the content of the line stored in the for replaceable parameter
for /f "delims=" %%a in ('command') do CLITool -Switch %%a
Also, you can execute the command, send the output to a temporary file and retrieve its contents with a input redirection and a set /p command
command > "tempfile"
<"tempFile" set /p "var="
CLITool -Switch %var%
In both cases, under the assumption that command will echo only one line. If it generates more lines, the for will execute CLITool for each of the lines, while the redirection option will only retrieve the first line from the temp file.
The third way to solve it is to use a pipe. The left side of the pipe generates the content and the right side reads this content.
command | ( set /p "var=" && #call CLITool -Switch %%var%% )
Why the call and the %%var%% syntax? We are setting a variable inside a line and in the same line we need to retrieve the variable and for this to work, delayed expansion is needed. But by default delayed expansion is disabled and enabling it does not solve anything as each side of the pipe is executed inside its own cmd instance that is started without delayed expansion (default behaviour) so, the trick is to escape the reference to the variable (the reason for the doubled percent sign) and force a double parse phase in the line (the reason for the call)
The alternative could be something as
command | cmd /e /v /q /c "set /p "var=" && CLITool -Switch !var!"
This way we are running in the right side of the pipt a cmd instance with delayed expansion enabled (/v), extensions enabled (/e), with echo off (/q) that will execute the needed command.
But the pipe code (in both cases) has a drawback: the variable is set in a separate cmd instance, so, your main batch file will no have access to its value.
If You have two lines being returned from an executable and wish to assign each to a variable, then
set "line1="&set "line2="
for /f "delims=" %%a in ('yourcommand') do if defined line1 (set "line2=%%a") else (set "line1=%%a")
set line
should save and display the results.
I have a bat file that does a bunch of things and closes the cmd window which is fine when user double clicks the bat file from explorer. But if I run the bat file from a already open cmd window as in cmd>c:\myfile.bat then I do not want the bat file to close the cmd window (END) since I need to do other things. I need bat dos command code that will do something like
if (initiated_from_explorer) then
else
endif
Is this possible ? thanks
mousio's solution is nice but I did not manage to make it work in an "IF" statement because of the double quotes in the value of %cmdcmdline% (with or without double quotes around %cmdcmdline%).
In constrast, the solution using %0 works fine. I used the following block statement and it works like a charm:
IF %0 == "%~0" pause
The following solution, which expands %~0 to a fully qualified path, might also work if the previous does not (cf. Alex Essilfie's comment):
IF %0 EQU "%~dpnx0" PAUSE
However, note that this solution with %~dpnx0 fails when
the .bat file is located somewhere in the %USERPROFILE% directory, and
your %USERNAME% contains one or more uppercase characters
because... wait for it... the d in %~dpnx0 forces your %USERPROFILE% username to lowercase, while plain %0 does not. So they're never equal if your username contains an uppercase character. ¯\_(ツ)_/¯
[Edit 18 June 2021 - thanks to JasonXA]
You can solve this lowercase issue with case-insensitive comparison (magic /I):
IF /I %0 EQU "%~dpnx0" PAUSE
This might be the best solution of all!
%cmdcmdline% gives the exact command line used to start the current Cmd.exe.
When launched from a command console, this var is "%SystemRoot%\system32\cmd.exe".
When launched from explorer this var is cmd /c ""{full_path_to_the_bat_file}" ";
this implicates that you might also check the %0 variable in your bat file, for in this case it is always the full path to the bat file, and always enclosed in double quotes.
Personally, I would go for the %cmdcmdline% approach (not %O), but be aware that both start commands can be overridden in the registry…
A consolidated answer, derived from much of the information found on this page:
:pauseIfDoubleClicked
setlocal enabledelayedexpansion
set testl=%cmdcmdline:"=%
set testr=!testl:%~nx0=!
if not "%testl%" == "%testr%" pause
The variable "testl" gets the full line of the cmd processor call (as per mousio), stripping out all of the pesky double quotes.
The variable "testr" takes "testl" and further strips outs the name of the current batch file name if present (which it will be if the batch file was invoked with a double-click).
The if statement sees if "testl" and "testr" are different. If yes, batch was double-clicked, so pause; if no, batch was typed in on command line, go on.
Naturally, if you want to do something else if you detect a double-click, you can change the pause.
Thanks everyone.
Less code, more robust:
Build upon the other answers, I find the most robust approach to:
Replace quotes with x to enable text comparison without breaking the IF statement.
Recreate the expected cmdcmdline exactly (well, with '"' replaced by x).
Test for case-insensitive equality.
The result is:
set "dclickcmdx=%comspec% /c xx%~0x x"
set "actualcmdx=%cmdcmdline:"=x%"
set isdoubleclicked=0
if /I "%dclickcmdx%" EQU "%actualcmdx%" (
set isdoubleclicked=1
)
This adds more robustness against general cmd /c calls, since Explorer adds an awkward extra space before the last quote/x (in our favor). If cmdcmdline isn't found, it correctly renders isdoubleclicked=0.
Addendum:
Similarly to the above method, the following one-liner will pause a script if it was double-clicked from explorer. I add it to the end of my scripts to keep the command-line window open:
(Edit 2022-01-12, fixed quote mismatching from this discussion)
if /i "%comspec% /c ``%~0` `" equ "%cmdcmdline:"=`%" pause
if /i "%comspec% /c %~0 " equ "%cmdcmdline:"=%" pause
Use exit /b 0, not exit
The former will exit all the way if launched from Windows Explorer, but return to the console if launched from the command line.
You can add a command line parameter when running from a CMD window that won't exist when the file is double-clicked. If there is no parameter, close the window. If there is, don't close it. You can test the parameter using %1
It's not only possible, but your desired behavior is the normal behavior of batch file execution, unless you do something 'special':
when executing a batch file by double-clicking it in Explorer, the cmd window will close when it's done;
when the batch file is executed from the command line, it simply returns to the command line prompt when complete - the window is not closed;
So I think the question that needs to be answered is what are you doing in the batch file that causes the command window to close when you execute it by the command line?
Like #anishsane I too wanted a pause statement if launched from explorer, but not when launched from a command window.
Here's what worked for me, based upon #mousio's answer above:
#SET cmdcmdline|FINDSTR /b "cmdcmdline="|FINDSTR /i pushd >nul
#IF ERRORLEVEL 1 (
#echo.
#echo Press ENTER when done
#pause > nul
)
(Nothing original here, just providing a working example)
Paste this at the beginning of your BAT or CMD script and maybe change what happens in the 'if' clause:
:: To leave command window open if script run from Windows explorer.
#setlocal
#set x=%cmdcmdline:"=%
#set x=%x: =%
#set y=%x:cmd/c=%
#if "%x%" neq "%y%" cmd /k %0 %* && exit || exit
#endlocal
What this does, is if the user either double-clicks or calls this script using "cmd /c" it will re-launch with "cmd /k" which will leave the session open after the command finishes. This allows the user to EXIT or maybe do something else.
The reason for doing it this way rather than the other ways explained in this answer is because I've found situations that still even with using the quotes or other symbols, the IF statement would barf with certain situations of the QUOTES and the /c and with spaces. So the logic first removes all QUOTES and then removes all spaces.. because SOMETIMES there is an extra space after removing the quotes.
set x=%cmdcmdline:"=% <-- removes all quotes
set x=%x: =% <-- removes all spaces
set y=%x:cmd/c=% <-- removes cmd/c from the string saving it to y
The point of the && exit || exit is so that if the ERRORLEVEL before exiting is 0 (success) it then stops running, but also if it is non 0 (some failure) it also stops running.
But you can replace this part:
cmd /k %0 %* && exit || exit
with something like
set CALLED_WITH_CMD_C=YES
and then make up your own differences in the rest of your script. You would have to then move or remove the endlocal.
The '#' symbol at front just prevents the echo, which you can have if you want to test.
Do not use echo on or echo off as it changes the setting and affects all subsequent scripts that call yours.
#dlchambers was close but set didn't work since cmdcmdline isn't a defined environment variable in some cases, but this version based on his works great for me:
echo %cmdcmdline% | findstr /i pushd >nul
if errorlevel 1 pause
after reading through the suggestions, this is what I went with:
set __cmdcmdline=%cmdcmdline%
set __cmdcmdline=%__cmdcmdline:"=%
set __cmdcmdline=%__cmdcmdline: =%
set __cmdcmdline=%__cmdcmdline:~0,5%
if "%__cmdcmdline%"=="cmd/c" set CMD_INITIATED_FROM_EXPLORER=1
set __cmdcmdline=
which conditionally sets the variable: CMD_INITIATED_FROM_EXPLORER
..and can subsequently be used as needed:
if defined CMD_INITIATED_FROM_EXPLORER (
echo.
pause
)
..but the issue regarding Powershell that #Ruben Bartelink mentions isn't solved:
running ./batch.cmd from Powershell uses cmd /c under the hood
You also can check for SESSIONNAME environment variable.
As you see here that variable typically isn't set in Explorer window. When invoking from cmd it SESSIONNAME is set to Console. I can confirm this for Windows 10.
Unfortunately behaviour seems to be changeable: https://support.microsoft.com/de-de/help/2509192/clientname-and-sessionname-enviroment-variable-may-be-missing
(Partly) Contrary and in addition to the accepted answer AToW (re %cmdcmdline%) and the top answer AToW (re if /i %0 equ "%~dpnx0") in Win10 it is:
in CMD:
in a *.cmd (here _pauseIfRunFromGUI.cmd):
"C:\Windows\System32\cmd.exe"
if /I _pauseIfRunFromGUI[.cmd] EQU "C:\Users\Geri\_pauseIfRunFromGUI.cmd"
.cmd is present if entered on the cmd line, which happens if you complete with Tab.
in a *.cmd (_pauseIfRunFromGUI.cmd) that's called by a *.cmd:
"C:\Windows\System32\cmd.exe"
if /I _pauseIfRunFromGUI[.cmd] EQU "C:\Users\Geri\_pauseIfRunFromGUI.cmd"
Same as above.
.cmd is present if called via call _pauseIfRunFromGUI.cmd.
In any way the comparison evaluates to false which is intended.
from GUI:
(Explorer and link on Desktop)
in a *.cmd (here _pauseIfRunFromGUI.cmd) that's launched from the GUI:
C:\WINDOWS\system32\cmd.exe /c ""C:\Users\Geri\_pauseIfRunFromGUI.cmd" "
if /I "C:\Users\Geri\_pauseIfRunFromGUI.cmd" EQU "C:\Users\Geri\_pauseIfRunFromGUI.cmd"
This one is different to the accepted answer AToW which says just cmd /c ""..." "_!
The comparison evaluates to true which is intended.
in a *.cmd (_pauseIfRunFromGUI.cmd) that's called by a *.cmd (here calling.cmd) that's launched from the GUI:
C:\WINDOWS\system32\cmd.exe /c ""C:\Users\Geri\calling.cmd" "
if /I _pauseIfRunFromGUI[.cmd] EQU "C:\Users\Geri\_pauseIfRunFromGUI.cmd"
Different to above, since calling .cmd is in cmdcmdline, of course, not the one in which it is evaluated (_pauseIfRunFromGUI.cmd).
.cmd is present if called via call _pauseIfRunFromGUI.cmd within calling.cmd.
The comparison evaluates to false which is not intended!
If the comparison is changed to:
if /i "%cmdcmdline:~0,31%"=="C:\WINDOWS\system32\cmd.exe /c " echo: & pause
everything works as expected.
Assuming Windows, is there a way I can detect from within a batch file if it was launched from an open command prompt or by double-clicking? I'd like to add a pause to the end of the batch process if and only if it was double clicked, so that the window doesn't just disappear along with any useful output it may have produced.
Any clever ways to do this? I'm looking for solutions I could rely on to work on a machine that was configured more or less with default settings.
I just ran a quick test and noticed the following, which may help you:
When run from an open command prompt, the %0 variable does not have double quotes around the path. If the script resides in the current directory, the path isn't even given, just the batch file name.
When run from explorer, the %0 variable is always enclosed in double quotes and includes the full path to the batch file.
This script will not pause if run from the command console, but will if double-clicked in Explorer:
#echo off
setlocal enableextensions
set SCRIPT=%0
set DQUOTE="
#echo do something...
#echo %SCRIPT:~0,1% | findstr /l %DQUOTE% > NUL
if %ERRORLEVEL% EQU 0 set PAUSE_ON_CLOSE=1
:EXIT
if defined PAUSE_ON_CLOSE pause
EDIT:
There was also some weird behavior when running from Explorer that I can't explain. Originally, rather than
#echo %SCRIPT:~0,1% | findstr /l %DQUOTE% > NUL
if %ERRORLEVEL% EQU 0 set PAUSE_ON_CLOSE=1
I tried using just an if:
if %SCRIPT:0,1% == ^" set PAUSE_ON_CLOSE=1
This would work when running from an open command prompt, but when run from Explorer it would complain that the if statement wasn't correct.
Yes. Patrick Cuff's final example almost worked, but you need to add one extra escape, '^', to make it work in all cases. This works great for me:
set zero=%0
if [^%zero:~0,1%] == [^"] pause
However, if the name of the batch file contains a space, it'll be double quoted in either case, so this solution won't work.
Don't overlook the solution of having two batch files:
abatfile.bat and abatfile-with-pause.bat
The second simply calling the first and adding a pause
Here's what I use :
rem if double clicked it will pause
for /f "tokens=2" %%# in ("%cmdcmdline%") do if /i "%%#" equ "/c" pause
I use a parameter "automode" when I run my batch files from scripts.
set automode=%7
(Here automode is the seventh parameter given.)
Some code follows and when the file should pause, I do this:
if #%automode%==# pause
One easy way to do it is described here:
http://steve-jansen.github.io/guides/windows-batch-scripting/part-10-advanced-tricks.html
There is little typo in the code mentioned in the link. Here is correct code:
#ECHO OFF
SET interactive=0
ECHO %CMDCMDLINE% | FINDSTR /L /I %COMSPEC% >NUL 2>&1
IF %ERRORLEVEL%==0 SET interactive=1
ECHO do work
IF "%interactive%"==1 PAUSE
EXIT /B 0
Similar to a second batch file you could also pause if a certain parameter is not given (called via clicking).
This would mean only one batch file but having to specify a -nopause parameter or something like that when calling from the console.
crazy idea: use tasklist and parse it's results.
I've wrote in a test batch file:
tasklist > test.out
and when I double-clicked it, there was an additional "cmd.exe" process just before the tasklist process, that wasn't there when the script was run from command line (but note that might not be enough if someone opens a command line shell and then double-click the batch file)
Just add pause regardless of how it was opened? If it was opened from command prompt no harm done apart from a harmless pause. (Not a solution but just thinking whether a pause would be so harmful / annoying )