Using two if statements - if-statement

#echo off
set Hint=3
echo Hints=%Hint%
pause >nul
set /p Talk=">"
if %Talk%"=="Hint?" &if %Hint%"=="3" echo yay!
pause >nul
cls
How do I make it say "yay!" using the two if statements (if you can) and only if %Hint%=3?
I am new to batch and are only a beginner.

Remove the ampersand & and add double quotes:
if "%Talk%"=="Hint?" if "%Hint%"=="3" echo yay!

Just remove the ampersand from your code.

if "%Talk%"=="Hint?" (
if "%Hint%"=="3" (
echo yay!
)
)
You're also missing some "'s

There are no binary logical operators in batch/CMD. You can chain them together, just like you would in C, though.
if "%Talk%"=="Hint?" if "%Hint%"=="3" echo yay!
If you needed more complex logic, you could use the bitwise AND (&) and OR (|) operators with SET /A to build a more complex expression and reduce it to a single "boolean" integer.

Related

How to use Findstr regex to match a dash (-)?

Trying to use findstr to match text that follows the format below:
PTB-14
AIR-217
The problem I'm having is that I just can't seem to get findstr to match on the dash, -. I've created the script below in a batch file:
set dash=-
echo.%dash%
echo !dash! | findstr /i /r /C:- >nul
if errorlevel 1 (
echo "ERROR!" >&2
)
I've tried the regex with /C:-, /C:"-", /C:"\-" and /C:"\\-". I just can't seem to get it matched. Anyone know what I am doing wrong?
Actually there is no need to use a regular expression (/R), but you can use a literal search string -. And case-insensitivity (/I) does also not make much sense with non-letter characters.
Anyway, I think the problem in your code is that you do not have delayed expansion enabled, although you are trying to use it, so echo !dash! actually echoes !dash! literally.
To solve that, there are a few options:
Place setlocal EnableDelayedExpansion before your code and (optionally) place endlocal after it in order to enable delayed expansion in the parent cmd instance that executes your batch file, like this:
setlocal EnableDelayedExpansion
set "dash=-"
echo(%dash%
echo(!dash!| > nul findstr /C:"-" || >&2 echo ERROR^^!
endlocal
A pipe | initiates two new cmd instances for either side, which do not have delayed expansion enabled, even if the parenth cmd instance does. However, you can explicitly initiate another cmd instance on the left side of the pipe with delayed expansion enabled (/V):
set "dash=-"
echo(%dash%
cmd /V /C echo(^!dash^!| > nul findstr /C:"-" || >&2 echo ERROR!
The exclamation marks are escaped (^!) in order for them not to be consumed by the parent cmd instance in case delayed expansion is enabled there; if not, the escaping does not harm.
In the above code fragments, I additionally changed the following:
set dash=- has become set "dash=-", as this is the most secure syntax that prevents unintended trailing white-spaces and accepts even special characters like ^, &, (, ), <, > and |;
echo. has become echo(, which is the only reliable syntax, although it looks odd;
the SPACE in front of the pipe | has disappeared, because it would also have been echoed;
if ErrorLevel 1 has been replaced by the conditional command concatenation operator ||, which lets the following command execute only in case an error occurred, or, technically spoken, in case the exit code of the previous command was non-zero;
echo "ERROR!" >&2 has been changed to >&2 echo ERROR^^! or >&2 echo ERROR! in order to avoid the quotation marks "" and the SPACE before >&2 to be echoed also; the double-escaping of the ! is needed to display it in case delayed expansion is enabled;

Bat file to replace all occurrences of a character in a filename with another

I need to do this on a list of a few thousand filenames such that the files are renamed with the replaced characters. The intent is to allow the following script to run on ALL names
The problem is names with the "&" sign.
I am using this script to locate all the filenames on a list containing several thousand names which has to be run against multiple folders full of the files to find just the ones on the list. If found I can either copy or move the file onto a flash-drive.
The script is working flawlessly except for names containing the "&". Unfortunately quite a few do. Other than finding a way to escape the & during the search, I could replace it with the any unique word or character long enough to run the search and reversing this after the files are found.
The initial script is like this:
set list=C:\ALIST.txt
pushd %source%
for /f "tokens=*" %%a in (%list%) do (echo.%%a && call :FindIt "%%~a")
goto :End
:FindIt
for /f "tokens=* usebackq" %%b in (`dir /b /s /a-d-s-h "%~1"`) do (call :DoIt "%%~dpb" "%~1")
goto :EOF
:DoIt
set str=%~1
set str=%str:~0,-1%
set file=%~2
rem NOTE: ADD /MOV at end of next line to move instead of COPY
robocopy "%str%" "%dest%" "%file%"
goto :EOF
:End
popd
This was written by a good friend and I am not taking credit for his work.
I looked everywhere to find a way to do this and his script was a Godsend that brought me 95% of the way to an end. The only problem with it is getting it to accept names with the "&" sign or short for that finding a way to replace the "&" sign temporarily long enough to run the script.
Any ideas or alternatives appreciated
The problem seems to be missing quotes
set "str=%~1"
set "str=%str:~0,-1%"
set "file=%~2"
But, without full knowledge of how/why this is done this way (probably it should be shorter), the full code can be written as
pushd %source% && (
for /f "tokens=*" %%a in (%list%) do (
echo(%%a
for /f "delims=" %%b in ('
dir /b /s /a-d-s-h "%%~a"
') do robocopy "%%~dpb\." "%dest%" "%%~a"
)
popd
)
without call and without substring operations.
This seems to work nicely though I can't vouch for spaces and special characters. Place this in a file called This.that.and.the.other.bat
#echo off
setlocal EnableDelayedExpansion
set src=%~n0
set "tgt=%src:.=!p! " & call set "tgt=!tgt!%"
#echo %src%
#echo %tgt%
endlocal

FINDSTR and RegEx issuse

I have a batch file that asks for input, stores this input in a var and then uses the var in a ping. I need to make sure that the input matches one of several naming conventions
Naming conventions:
PCX1 can be as high as 100
GENPRT1 can be as high as 100
NETPRT1 can be as high as 100
FAXPRT1 can be as high as 100
So if i enter 12 it will not work but if I enter PCX12 it will.
Everything in the script works except the regex. How can i get this to work?
if "%sta%" == "findstr %sta% ^PCX[0-9]*[0-9]*[0-9]$ \i" (
echo The syntax is correct
goto PING
) else (
set errmsg=The syntax is wrong
goto START
)
This should help:
^(PCX|GENPRT|NETPRT|FAXPRT)([\d]|0[\d]|00[\d]|0[\d][\d]|[\d][\d]|100)$
FINDSTR's regex flavor is extremely limited. It doesn't even support alternation (|), so even very simple problems are going to have very messy solutions. Here's the most compact expression I can come up with:
FINDSTR /R /I "^PCX[1-9][0-9]?$ ^PCX100$ ^GENPRT[1-9][0-9]?$ ^GENPRT100$ ^NETPRT[1-9][0-9]?$ ^NETPRT100$ ^FAXPRT[1-9][0-9]?$ ^FAXPRT100$"
Each space-separated sequence is treated as a separate regex, so this tries to perform up to eight matches on each string it tests. That's not to say it's slow, but it's a pain in the butt to use when you're used to real regexes.
For reference, here's how I would have written that in a serous regex flavor:
^(PCX|((GEN|NET|FAX)PRT))([1-9][0-9]?|100)$
If you have the option of using a different tool (like PowerShell, which uses .NET's very powerful and feature-rich regex flavor), I strongly recommend you do so.
#echo off
setlocal disabledelayedexpansion
:start
set /p "sta=What ? "
cmd /v /d /q /c "(echo(!sta!)" ^
| findstr /i /r /b /e "PCX[0-9]* GENPRT[0-9]* NETPRT[0-9]* FAXPRT[0-9]*" ^
| findstr /r /e "[^0-9][1-9] [^0-9][1-9][0-9] [^0-9]100" > nul
if errorlevel 1 (
echo The syntax is wrong
goto :start
)
echo The syntax is correct
A new cmd instance is used to ensure the tested string will not include any parser added space at the end. The output of the echo command is tested to see if it matches any of the starting strings followed by numbers up to the end. Then it is tested again for a valid number range.
If errorlevel is set, the value does not match the condition and a new value is requested.
If errorlevel is not set, the value is correct.

Windows Batch - check if string starts with ... in a loop

this grabs the output from a remote branch list with git::
for /f "delims=\" %r in ('git branch -r') do (
::then in here I want to get rid of the origin/HEAD -> line of the output
::and do a few git ops on the other lines, which are the names of branches
)
anyhow, I'm finding this frustrating as apparently batch doesn't have regex
here's the code I'm using to do this in bash
for remote in `git branch -r | grep -v '\->'`;
do echo $remote; #...git stuff
done;
the grep removes the "origin/HEAD -> origin/master" line of the output of git branch -r
So I'm hoping to ask how to implement the 'contains' verb
for /f "delims=\" %r in ('git branch -r') do (
if not %r contains HEAD (
::...git stuff
)
)
on a loop variable
this stackoverflow question appears to answer a similar question, although in my attempts to implement as such, I became confused by % symbols and no permutation of them yielded function
EDIT FOR FUTURE READERS: there is some regex with findstr /r piped onto git branch -r
for /f "delims=\" %%r in ('git branch -r^|findstr "HEAD"') do (
echo ...git stuff %%r
)
should give you a start.
Note: %%r, not %r within a batch file - %r would work directly from the prompt.
Your delims=\ filter will produce that portion up to the first \ of any line from git branch -r which contains HEAD - sorry, I don't talk bash-ish; you'd need to say precisely what the HEAD string you want to locate is.
Use "delims=" fo the entire line - omitting the delims option will set delimiters to the default set (space, comma, semicolon, etc.)
Don't use ::-comments within a block (parenthesised statement-sequence) as it's actually a broken label and cmd doesn't appeciate labels within a block. Use REM comments here instead.
The resultant strings output from the findstr (which acts on a brain-dead verion of regex) will be processed through to the echo (or whatever statement you may substitute here) - if there are none, the for will appear to be skipped.
Quite what your target string would be for findstr I can't tell. From the prompt, findstr /? may reveal. You may also be able to use find (find /?) - but if you are using cygwin the *nix version of find overrides windows-native.
I don't know what the git branch output looks like, but with a test case of
test 1
HEAD test \-> 2
test 3
test 4
the following prints all the text lines except the one containing \->
#setlocal enableextensions enabledelayedexpansion
#echo off
for /f "tokens=*" %%r in (d:\test2.txt) do (
set str1=%%r
if "!str1:\->=!"=="!str1!" (
echo %%r
)
)
The if test is fundamentally doing this test: string1.replace("HEAD", "") == string1.
Your loop variable needs to be %r if used directly in the command prompt, but %%r if in a batch file.
The string replacement is a part of environment variables, not loop variables, so it needs to be put into a holding string (str1) to work with. If you have the command extensions enabled ( enableextensions ).
And because environment variable setting operations happen when the script is read, you need to override that with enabledelayedexpansion and using !str1! instead of %str1%, otherwise the value of str1 won't change from one loop to the next.
(PS. Use PowerShell instead. Get-Content D:\test2.txt | Select-String "\->" -NotMatch ).

Piped Variable Into FINDSTR w/ Regular Expressions and Escaped Double Quotes

I am trying to understand a batch file that was sent to me in order to work around a bug in a third party program while they resolve the issue. Basically they are running a findstr regular expression command in order to determine whether or not the string matches. If it does, then the special characters that should not be stripped out are being added back in manually before it is passed off to the original commandline program.
As best I can tell though, what has been provided does not work or I do not understand it. I am pasting the relevant section of code below.
#echo off
setlocal
set username=%1
shift
echo %username% | findstr /r "^\"[0-9][0-9]*\"" >nul
if not errorlevel 1 (set username=";%username:~0,9%=%username:~10,4%?")
echo %username%
The three pieces I really have questions about are as follows:
I believe the unescaped interpretation of the regular express above is ^"[0-9][0-9]*" which I think means that the string must begin with a numeric character and then must consist of zero or more additional numeric-only characters in order for a match to be found. Well, FINDSTR seems to be doing something weird with the escaped quotes and I cannot get it to match anything I have tried. If I remove the \" around [0-9][0-9]* then I can get it to work, but it does not properly reject non-numeric characters such as an input string of 123456789O1234 (there is a letter O instead of a zero in that sample string).
What is the point of the >nul
Wouldn't it be better to check for an errorlevel equal to 0 instead of "not errorlevel 1" since it could possibly return an error level of 2?
Anyway, the following code works, but it is not as precise as I would like. I am just looking to understand why the quotes in the regex string are not working. Perhaps this is a limitation of FINDSTR, but I have not came across anything definitive yet.
#echo off
setlocal
set username=%1
shift
echo %username% | findstr /r "^[0-9][0-9]*" >nul
if not errorlevel 1 (set username=";%username:~0,9%=%username:~10,4%?")
echo %username%
I can workaround the problem by repeating the class 14 times since that is the number of characters in my situation (more than 15 classes will cause it to crash - scroll to the bottom). I am still curious as to how this could be achieved more simply, and of course the remaining 2 questions.
EDIT / WORKING SOLUTION
#echo off
setlocal enableDelayedExpansion
set username=%~1
shift
echo !username!|findstr /r /c:"^[0-9][0-9]*$" >nul
if not errorlevel 1 (set username=";!username:~0,9!=!username:~10,4!?")
echo !username!
NOTES:
When I first ran it after modifying my existing code to more cloesly resemble dbenham's, enableDelayedExpansion gave an error as did the quotes around setting the username (see below). I can't replicate what I did wrong, but it is all working now (this is in case someone else comes across the same issue).
I had tried the $ for the EOL marker (which is the key to forcing it match numeric content only), but I think that the other problems were getting in the way which made me think it was not the solution. Also, to ensure the $ works don't miss this part of dbenham's answer "...you must also make sure there are no spaces between your echoed value and the pipe symbol."
In short it pretty much seems that trying to put double quotes inside a regex for findstr is wrong syntax/does not work/etc... unless you are actually looking to match " in the string/files you are parsing through. See dbenham's answer for clarity here. As he noted, you can use %~1 to strip the quotes from the argument instead of adding it to your regex (and programmatically add them back in if needed).
Error Message
C:>sample.bat 123456789
'enableDelayedExpansion' is not recognized as an internal or external command,
operable program or batch file.
'"' is not recognized as an internal or external command,
operable program or batch file.
!username!
Reference Links:
Undocumented features and limitations of the Windows FINDSTR command
Case sesntive anomalies with findstr (not handling case properly in some circumstances)
http://ss64.com/nt/findstr.html
http://www.robvanderwoude.com/findstr.php
http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/findstr.mspx
Answering your questions in reverse order:
3) if not errorlevel 1 is probably the same as if %errorlevel%==0 because IF ERRORLEVEL 1 means if ERRORLEVEL is greater than or equal to 1. So putting a NOT in front means if ERRORLEVEL is less than 1. I believe FINDSTR never returns a negative ERRORLEVEL, so the syntax should be OK.
2) The >nul redirects the stdout output of FINDSTR to the nul device, meaning it disables the output. Normally any matching line would be printed. You are only interested in the return code - you don't want to see the output.
1) The original regex will match any input string that starts with a quote, followed by at least one digit, followed by another quote. It ignores any characters that may appear after the 2nd quote.
So the following strings (quotes included) will match:
"0"
"01234"
"0"a
"01234"a
The following strings will not match:
0
01234
""
"0a"
The original code has problems if the number of digits in the matching string reaches a certain length because the ending quote gets stripped causing the closing ) to be quoted and so the rest of the script fails.
I don't understand your requirements so I don't know how to fix the code.
It sounds like you don't want to match strings that have non digits. That means you need to include the end of line marker $ at the end of the regex. But you must also make sure there are no spaces between your echoed value and the pipe symbol.
I believe you probably don't want quotes in your value, (or else you should programatically add them at the very end). You can use %~1 to strip any enclosing quotes from the supplied argument.
If you are looking to check if argument 1 consists of nothing but numeric digits, then you can use:
setlocal enableDelayedExpansion
set "username=%~1"
echo !username!|findstr /r "^[0-9][0-9]*$" >nul
I used delayed expansion because you have no control over what characters are in %1, and if it contains special characters like & or | it will cause problems if you use normal expansion. The syntax I have given is not bullet proof, but it handles most "normal" situations.
It is not necessary in your case, but I prefer to use the /c option, just in case your search string contains spaces. So the above could be written as
echo !username!|findstr /r /c:"^[0-9][0-9]*$" >nul
It seems odd to me that both the original and your modified code simply pass through the username if it does not match your regex. Maybe that is your intent, maybe not.