Batch File If Statements causing close of CMD and not working - if-statement

I am creating a should-be-simple batch file that will allow me to input a class name and it will take me to the correct google classroom. However, my if statement doesn't work, even when I input the word "Social Studies". It does not take me to my classroom, and on top of that, the CMD is just closed. When I remove the If Statement line, the code works fine and the cmd just stays open after inputting a class.
set /p class="Enter Class: "
IF "%class%" /I EQU "Social Studies" (START https://classroom.google.com)
cmd /k

IF /I "%class%" EQU "Social Studies"...
The parsing logic for an if statement is very specific; if [/i][NOT] arg1 op arg2 where /i and not are optional, but must if used, be used in that order.
Your code sees /i where it expects a comparison-operator and generates a syntax-error.
When you use the point-click-and-giggle method of executing a batch, the batch window will often close if a syntax-error is found. You should instead open a 'command prompt' and run your batch from there so that the window remains open and any error message will be displayed.

You can write #echo off whice prevents the prompt and contents of the batch file from being displayed.
I replaced the your EQ with == and now it works:
#echo off
set /p class="Enter Class: "
IF "%class%"=="Social Studies" (START https://classroom.google.com)
PAUSE
The PAUSE at the end will make the CMD remain open after it's done

Related

How to create a .bat file to enable or disable a specific audio device with an if statement

With my little knowledge of writing code, I am trying to make a batch file where I can disable or enable a line in audio device with an IF statement.
For example, if the device is currently enabled, when I run the .bat file it will disable. If it's disabled, then it enables it.
I have the the device ID ("SWD\MMDEVAPI{0.0.1.00000000}.{55e90a30-8001-4bc8-af56-52998c03ed88}"). I am currently using pnputil /disable-device which works. But I don't know how to go about the IF statement. Would appreciate some help with that.
Edit: Okay so I found a tool called SoundVolumeView.exe that can lower the sound so the white noise can't be heard on startup. It can also mute as well all through command line.
I wasn't sure if you had solved this yourself already and I know I'm not supposed to spoon feed code on here but I had a script that did something similar and thought I should contribute. If you have any issues with this script, please comment with your issue.
Explaination: it runs pnputil /enum-devices /ids and looks at the output until it gets to a line with the device id. It sets a variable called ahead to 4. If ahead is above 0, then it will check if it is 0, if it isn't then it will go to the next line in the output of the command. If it is 0, then it will go to the toggle label and then it will check the line if it doesn't say Started. If it doesn't have Started in the line, then it will enable your device, if it does say started, then it will disable it. Then it just pauses and exits.
#echo off
setlocal enableDelayedExpansion
REM Needs to be run as administrator to work
set device=SWD\MMDEVAPI{0.0.1.00000000}.{55e90a30-8001-4bc8-af56-52998c03ed88}
set ahead=-1
for /f "delims=" %%a in ('pnputil /enum-devices /ids') do (
set "line=%%a"
if !ahead! GEQ 0 (
if !ahead!==0 goto :toggle
set /a ahead-=1
) else (
if not "x!line:%device%=!"=="x!line!" (
set ahead=4
)
)
)
:toggle
if "x!line:Started=!"=="x!line!" (
REM Device is not running
pnputil /enable-device "%device%" >nul
echo Enabling device...
) else (
REM Device is running
pnputil /disable-device "%device%" >nul
echo Disabling device...
)
pause
exit /b 0
For documentation on PnPUtil: https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/pnputil-command-syntax
For information on on for loops: https://ss64.com/nt/for.html, https://stackoverflow.com/a/50704943/19341457
For information on finding if a substring is in a string: https://stackoverflow.com/a/7006016/19341457
For information on delayed expansion: https://ss64.com/nt/delayedexpansion.html

Windows Batch - how to add timestamp while redirecting stdout to file

I'm doing make all -d --trace
How do I get Gnu Make to output timestamps for every line it outputs?
More generally, how do I add a timestamp to every STDOUT and STDERR statement?
There is a solution for Linux/Bash but I'm on Windows.
I created a one line batch file add_ts.bat : echo %time% %1
I tried the following but I only got one timestamp (without the lines that were output):
make all --trace -d 2>&1 | add_ts.bat
To a first approximation you need a batch file like:
add_ts.bat
#for /F "usebackq delims==" %%i in (`%1`) do #echo %time% %%i
which you would run like:
add_ts.bat "make all -d --trace" > buildlog.txt
This however isn't good enough if you want to capture and
timestamp STDERR as well as STDOUT from the command passed as
%1, because the backticks operator around %1 will only capture STDOUT
To fix this you'll need to capture STDERR as well as STDOUT within the backticks, by using redirection in there, which in turns means
you need to run a subshell to understand the redirection, and you need to
escape the redirection operators so they're not interpreted by the toplevel
shell. Which all comes to:
#for /F "usebackq delims==" %%i in (`cmd /C %1 2^>^&1`) do #echo %time% %%i
Run just the same way.
Later
what I don't get is why the | itself wasn't enough to send STDOUT and STDERR to STDIN of add_ts.bat
It is. I think you are labouring under the combination of two misconceptions.
One: You believe that a program's commandline arguments are the same as its standard
input, or that it gets it commandline arguments from its standard input. It doesn't.
A program's commandline arguments, if any, are passed to it as a fixed list
of strings in the program-startup protocol. Its standard input is an input stream made
available to it at the same time by the OS and connected by default to the console in which the program
starts. This default can be overridden in the shell by redirection operators. The contents of that input stream are not fixed in advance. It will feed to the
the program whatever is input to the console, or from its redirected proxy, as long as the program is running, as and when the program reads it. The program
can parse or ignore its commandline arguments and, quite independently of that, it can read or ignore its standard input.
Your program add_ts.bat is a program that parses the first of its commandline arguments
- it uses %1 - and ignores any more. And it ignores its standard input entirely.
Two: You believe that the effect of a pipeline, e.g.
a | b
is to start an a process and then, for each line that it writes to the standard output, start
a distinct b process which will automatically receive that one line written by a as
a single commandline argument (no matter who many words are in the line) and do its stuff
with that single commandline argument.
That's not what happens. The OS starts one a process and one b process, and connects the
standard output of the one a process to the standard input of the one b process. For the
pipeline to work at all, b has got to be a program that reads its standard input. Your
add_ts.bat is not such a program. It's only a program that parses its first commandline
argument: the | doesn't give it any, and the commandline:
make all --trace -d 2>&1 | add_ts.bat
doesn't give it any either. The commandline:
make all --trace -d 2>&1 | add_ts.bat "Hello World"
would give it one commandline argument and:
make all --trace -d 2>&1 | add_ts.bat Hello World
would give it two, not one, commandline arguments, the second being ignored. But in any case
it doesn't read its standard input so piping to it is futile.
The site ss64.com is perfectly good about CMD redirection and piping
but it assumes you know what a program has to do to be a pipeline-able command: To be an upstream command,
it has to write its standard output; to be a downstream command it has to read its standard input.
Using a batch file wrapper is a clever solution if you don't mind the extra overhead. Otherwise I think you'll have to modify GNU make itself to have it print out this data.
If that's not palatable for some reason, you can get that information by using ElectricMake, a GNU-make-compatible implementation of make that includes lots of enhancements, including annotated build logs that have microsecond-resolution timestamps for every job in the build. ElectricMake is part of ElectricAccelerator Huddle.
Here's a bit of the annotation for a trivial "echo Hello World!" job:
<job id="J00007fb820002000" thread="7fb82f7fe700" start="3" end="4" type="rule" name="all" file="
Makefile" line="1">
<command line="2">
<argv>echo Hello, world!</argv>
<output src="prog">Hello, world!
</output>
</command>
<commitTimes start="0.291693" wait="0.296587" commit="0.296628" write="0.296680"/>
<timing invoked="0.291403" completed="0.296544" node="ecdroid3a-59"/>
</job>
Here, the <timing> tag shows the start time (0.291403 seconds) and end time (0.296544 seconds) of the job relative to the start of the build.
These annotated build logs can be viewed and analysed graphically with ElectricInsight, a companion tool for ElectricMake.
ElectricAccelerator Huddle is the freemium version of ElectricAccelerator -- usage is entirely free up to a point, with modest pay-as-you-go fees beyond that
Disclaimer: I'm the architect of ElectricAccelerator.

Windows Command Line Processor: Multiple and Nested IF Statements

Intended software: windows command line processor (version 6.1.7601.17514)
Hi,
I've been trying to build a multiple-statement command line that runs within a short-cut. My goal is to be able to click one short-cut that checks if my hosted network is started or not, and then takes appropriate action based on the check. The code that starts and stops the hosted network is fine, and for the most part, the logic works, but I notice odd behavior when I check the outputs of the logic. I suspect that my problem has to do with the way I structured the statements, but I'm having difficulty properly interpreting the built-in documentation and the documentation I can find in the MSDN library. If it's possible, I want to avoid using batch files for this solution.
To keep things simple, I've substituted my lengthy "netsh" commands with "echo" commands that show the errorcode. The code below is what I'm using to test my logic:
Test Code
netsh wlan show hostednetwork | find "Not" && echo found %errorlevel% || echo lost %errorlevel%
Currently, the way I'm reading this is:
Show me hostednetwork's status and send the output to input
Attempt to find the string "Not" in the input
If the attempt succeeds, output "found" and the errorcode to the screen
If the attempt fails, then output "lost" and the errorcode to the screen
Notice that I'm not using any flags on the find command. I'm doing this because I want to reduce the chance of finding a false match. To clarify what I mean, I'll show the output if I just put in
netsh wlan show hostednetwork:
Sample Output of Hostednetwork Status
C:\Windows\system32>netsh wlan show hostednetwork
Hosted network settings
-----------------------
Mode : Allowed
SSID name : "TestHost"
Max number of clients : 100
Authentication : WPA2-Personal
Cipher : CCMP
Hosted network status
---------------------
Status : Not started
If I search for the string "Not", then that's sufficient to tell me that the hosteadnetwork is not started, because when the hosteadnetwork is started, the output shows "Started".
The way I'm simulating the conditions of the hostednetwork is with the following commands:
netsh wlan start hostednetwork
netsh wlan stop hostednetwork
I expect that when I open a command prompt (as an administrator):
If the hostednetwork is not started, I should see a "found 0" in the output, meaning that the string was found and that there were no errors.
If the hostednetwork is started, I should see a "lost 1" in the output, meaning that the string was not found and that there was an error.
Case #1 works, but case #2 doesn't work on the first try. Here's my output when the hostednetwork is already started:
Output With Hostednetwork Started
C:\Windows\system32>netsh wlan start hostednetwork
The hosted network started.
C:\Windows\system32>netsh wlan show hostednetwork | find "Not" && echo found %er
rorlevel% || echo lost %errorlevel%
lost 0
C:\Windows\system32>netsh wlan show hostednetwork | find "Not" && echo found %er
rorlevel% || echo lost %errorlevel%
lost 1
Other Attempted Solutions
The way I've written the test code is the best I could come up with so far. In previous attempts, I've tried:
Setting a custom variable instead of using the errorlevel variable, but I get the same output on case #2.
Changing the code into an if else equivalent, but that didn't pan out very well.
Wrapping the conditional statements in brackets "()" after the pipe and using different combinations of the special symbols "&" and "|".
Other Questions
This question is related to another that I've been trying to figure out. If I wanted to search for three different strings in a command's output and exit on a different error code for each string, how can I do this? The syntax below is my starting point:
myCommand [/options] | ((find "string1" && exit /b 2 || ver>nul) &&
(find "string2" && exit /b 3 || ver>nul) && (find "string3" && exit /b 4 || ver>nul))
For the same reasons above, I didn't use any flags on the "find" commands. Also, I used "ver>nul" in an attempt to keep the syntax correct since I know the "ver" operation succeeds.
Any assistance is appreciated.
I don't understand why you want to avoid use of a batch script. Your shortcut can simply point to a small batch script, and life will be much easier.
But it is possible to do what you want. The value of %errolevel% is determined during parsing, and the entire shortcut is parsed in one pass, so you get the value that existed prior to execution of your FIND commands. You need delayed expansion !errorlevel! to get your desired results.
In batch you use setlocal enableDelayedExpansion, but that does not work from the command line (or a shortcut). Instead you must instantiate an extra CMD.EXE with the /V:ON option.
netsh wlan show hostednetwork | cmd /v:on /c "find "Not" && echo found !errorlevel! || echo lost !errorlevel!"
There are multiple levels of quoting going on, and that can sometimes cause problems. You can eliminate the quotes enclosing the command if you escape the special characters.
netsh wlan show hostednetwork | cmd /v:on /c find "Not" ^&^& echo found !errorlevel! ^|^| echo lost !errorlevel!
Regarding your 2nd question, I see 2 problems.
1) I don't understand the point of having a shortcut designed to exit with different error codes. How can you possibly make use of the returned error code?
2) You cannot pipe content into multiple FIND commands. The first FIND command will consume all the content and close the pipe, and then subsequent FIND commands will wait indefinitely for content from the keyboard.
You would have to redirect your command output to a temp file, and then redirect input of each FIND command to the temp file.
You cannot evaluate a variable in the same line. It needs delayed expansion and !errorlevel! to be used.
Do it in a batch file and you won't have a problem using delayed expansion.

Batch Script Input Problems

I have two questions regarding a batch script I'm working on. I realize that batch script questions are common but haven't found an answer to my exact question so I thought I'd try asking. The problematic areas are the user input sections on the menus.
There are two problems: 1) Input entered that is not one of the specified choices will cause the script to jump to random areas. And 2) some sections that use external programs are not taking the user %input% even when I know the syntax and flag use would normally be correct (as in, I can run them manually... so for some reason the input isn't capturing on them).
First issue example:
:MenuOne
echo Select one of the following options:
echo 1) x
echo 2) y
echo Q) Quit
set INPUT=
set /P INPUT=[1,2,Q]: %=%
If "%INPUT%"=="1" goto xoption
If "%INPUT%"=="2" goto yoption
If /I "%INPUT%"=="Q" goto Quit
:xoption
#REM Here goes a lot more submenus and/or options that actually run tools via cmd.
:yoption
#REM Again, menus and/or tools being invoked, in a listed menu, designed like above.
:Quit
echo Quitting...
exit
If a user types "b" at the selection prompt, I would love for the script to give an error and repeat the menu. Instead it jerks around other menus. I'm guessing that I need some ELSE statements? Does anyone have an example that I can use to accomplish this?
Second issue of some commands not using the %input% properly and returning an error as though it never received the %input%.
set /P INPUT=[Testone Input]: %testone%
set /P INPUT=[Testtwo Input]: %testtwo%
commandtorun.exe -f %testone% -h %testtwo%
Thanks!
Better to use choice (http://ss64.com/nt/choice.html) because it will persist until you set the correct input
CHOICE /C XYQ /M "Select of the following options [X,Y,Q]"
if errorlevel 1 goto :x
if errorlevel 2 goto :y
uf errorlevel 3 goto :q
Yet it's still possible to be done with IFs
set INPUT=
set /P INPUT=[1,2,Q]: %=%
If "%INPUT%"=="1" goto xoption
If "%INPUT%"=="2" goto yoption
If /I "%INPUT%"=="Q" goto Quit
rem -- will be executed only if the all the above are not true
goto :eof
For the second problem..You are not using SET /P correctly (the name of the variable should be in the front) , or you are trying something that I don't understand (where input variable is used):
set /P testone=[Testone Input]:
set /P testtwo=[Testtwo Input]:
commandtorun.exe -f %testone% -h %testtwo%
In your program as written, all your choices will fall through to the next one. If no relevant choice is entered, it will run :xoption and :yoption. Each of those should probably return to the menu after executing:
:MenuOne
echo Select one of the following options:
echo 1) x
echo 2) y
echo Q) Quit
set INPUT=
set /P INPUT=[1,2,Q]: %=%
If "%INPUT%"=="1" goto xoption
If "%INPUT%"=="2" goto yoption
If /I "%INPUT%"=="Q" goto Quit
echo Invalid selection.
echo.
goto MenuOne
:xoption
#REM Here goes a lot more submenus and/or options that actually run tools via cmd.
goto MenuOne
:yoption
#REM Again, menus and/or tools being invoked, in a listed menu, designed like above.
goto MenuOne
A real simple way to ensure a valid selection is made is to use the choice command instead of set /P. That will force the user to enter a value:
choice -c 12Q
echo %errorlevel%
The choice command will return the index of the selected character (1, 2 or 3 in the above example). A bonus is that it is case-insensitive, so you don't have to worry about checking both Q and q.

Can I have an IF block in DOS batch file?

In a DOS batch file we can only have 1 line if statement body? I think I found somewhere that I could use () for an if block just like the {} used in C-like programming languages, but it is not executing the statements when I try this. No error message either. This my code:
if %GPMANAGER_FOUND%==true(echo GP Manager is up
goto Continue7
)
echo GP Manager is down
:Continue7
Strangely neither "GP Manager is up" nor "GP Manager is down" gets printed when I run the batch file.
You can indeed place create a block of statements to execute after a conditional. But you have the syntax wrong. The parentheses must be used exactly as shown:
if <statement> (
do something
) else (
do something else
)
However, I do not believe that there is any built-in syntax for else-if statements. You will unfortunately need to create nested blocks of if statements to handle that.
Secondly, that %GPMANAGER_FOUND% == true test looks mighty suspicious to me. I don't know what the environment variable is set to or how you're setting it, but I very much doubt that the code you've shown will produce the result you're looking for.
The following sample code works fine for me:
#echo off
if ERRORLEVEL == 0 (
echo GP Manager is up
goto Continue7
)
echo GP Manager is down
:Continue7
Please note a few specific details about my sample code:
The space added between the end of the conditional statement, and the opening parenthesis.
I am setting #echo off to keep from seeing all of the statements printed to the console as they execute, and instead just see the output of those that specifically begin with echo.
I'm using the built-in ERRORLEVEL variable just as a test. Read more here
Logically, Cody's answer should work. However I don't think the command prompt handles a code block logically. For the life of me I can't get that to work properly with any more than a single command within the block. In my case, extensive testing revealed that all of the commands within the block are being cached, and executed simultaneously at the end of the block. This of course doesn't yield the expected results. Here is an oversimplified example:
if %ERRORLEVEL%==0 (
set var1=blue
set var2=cheese
set var3=%var1%_%var2%
)
This should provide var3 with the following value:
blue_cheese
but instead yields:
_
because all 3 commands are cached and executed simultaneously upon exiting the code block.
I was able to overcome this problem by re-writing the if block to only execute one command - goto - and adding a few labels. Its clunky, and I don't much like it, but at least it works.
if %ERRORLEVEL%==0 goto :error0
goto :endif
:error0
set var1=blue
set var2=cheese
set var3=%var1%_%var2%
:endif
Instead of this goto mess, try using the ampersand & or double ampersand && (conditional to errorlevel 0) as command separators.
I fixed a script snippet with this trick, to summarize, I have three batch files, one which calls the other two after having found which letters the external backup drives have been assigned. I leave the first file on the primary external drive so the calls to its backup routine worked fine, but the calls to the second one required an active drive change. The code below shows how I fixed it:
for %%b in (d e f g h i j k l m n o p q r s t u v w x y z) DO (
if exist "%%b:\Backup.cmd" %%b: & CALL "%%b:\Backup.cmd"
)
I ran across this article in the results returned by a search related to the IF command in a batch file, and I couldn't resist the opportunity to correct the misconception that IF blocks are limited to single commands. Following is a portion of a production Windows NT command script that runs daily on the machine on which I am composing this reply.
if "%COPYTOOL%" equ "R" (
WWLOGGER.exe "%APPDATA%\WizardWrx\%~n0.LOG" "Using RoboCopy to make a backup of %USERPROFILE%\My Documents\Outlook Files\*"
%TOOLPATH% %SRCEPATH% %DESTPATH% /copyall %RCLOGSTR% /m /np /r:0 /tee
C:\BIN\ExitCodeMapper.exe C:\BIN\ExitCodeMapper.INI[Robocopy] %TEMP%\%~n0.TMP %ERRORLEVEL%
) else (
WWLOGGER.exe "%APPDATA%\WizardWrx\%~n0.LOG" "Using XCopy to make a backup of %USERPROFILE%\My Documents\Outlook Files\*"
call %TOOLPATH% "%USERPROFILE%\My Documents\Outlook Files\*" "%USERPROFILE%\My Documents\Outlook Files\_backups" /f /m /v /y
C:\BIN\ExitCodeMapper.exe C:\BIN\ExitCodeMapper.INI[Xcopy] %TEMP%\%~n0.TMP %ERRORLEVEL%
)
Perhaps blocks of two or more lines applies exclusively to Windows NT command scripts (.CMD files), because a search of the production scripts directory of an application that is restricted to old school batch (.BAT) files, revealed only one-command blocks. Since the application has gone into extended maintenance (meaning that I am not actively involved in supporting it), I can't say whether that is because I didn't need more than one line, or that I couldn't make them work.
Regardless, if the latter is true, there is a simple workaround; move the multiple lines into either a separate batch file or a batch file subroutine. I know that the latter works in both kinds of scripts.
Maybe a bit late, but hope it hellps:
#echo off
if %ERRORLEVEL% == 0 (
msg * 1st line WORKS FINE rem You can relpace msg * with any othe operation...
goto Continue1
)
:Continue1
If exist "C:\Python31" (
msg * 2nd line WORKS FINE rem You can relpace msg * with any othe operation...
goto Continue2
)
:Continue2
If exist "C:\Python31\Lib\site-packages\PyQt4" (
msg * 3th line WORKS FINE rem You can relpace msg * with any othe operation...
goto Continue3
)
:Continue3
msg * 4th line WORKS FINE rem You can relpace msg * with any othe operation...
goto Continue4
)
:Continue4
msg * "Tutto a posto" rem You can relpace msg * with any othe operation...
pause