Set commands after an If command - if-statement

Trying to make a quiz show.bat file and having trouble with the If-Set coding:
:10
echo #: (Press any key to continue)
pause >nul
cls
echo Bonus Question 1:
echo Translate the letters in CAPS.
echo #: Copy the sentence and translate the words in CAPITALS.
echo Il nome di mia ZIA e Gabriella e il nome di mio ZIO e Nick.
echo #: Careful, you only get 3 goes!
echo #: now you try!
set /p BonusAnswer1=">"
set /p BonusAnswer1=">"
if "%BonusAnswer1%"=="Il nome di mia aunty Gabriella e il nome di mio uncle Nick. echo well done! &goto:11
if "%BonusAnswer1%"=="Hint" echo #: Copy the sentence and translate ZIA and ZIO. Only put a capital for Il, Gabriella and Nick. &goto:10
here is where I have the problem:
if "%Bonus1Tries%"=="3" set Bonus1Tries=2 &echo you only have %Bonus1Tries% left!
the only part of that coding that isn't working is it says:
you only have 3 tries left!
instead of
you only have 2 tries left!
which means that
set Bonus1Tries=2
isn't working.
Can you help please and thank you in advance! -Batch Man

inside a code block you need delayed expansion, or you split the command (if possible):
if "%Bonus1Tries%"=="3" set "Bonus1Tries=2"
if "%Bonus1Tries%"=="2" echo you only have %Bonus1Tries% left!

You have to enable delayed expansion
SETLOCAL ENABLEDELAYEDEXPANSION
set VAR=before
set VAR=after & echo immediate:%VAR%, delayed:!VAR!
ENDLOCAL
As explained by Raymond Chen

Related

All letters, numbers and symbols in one?

Hey does anyone know if there is a command or something that allows me to have all the letters, numbers and characters in one?
:start
set input=
set /p input=[Y / N]?
if %input%==Y goto y
if %input%==N goto n
if %input%==* goto this
:y
goto end
:n
exit
:this
#echo. Only J or N
:end
Here you can already see that I tried to get everything in one with "*" which unfortunately didn't work...
Thank you for trying to help me but I would like to point out again that I need help to find something that allows me to use all the characters, letters and symbols in one.
example:
if I take y then he goes on.
if I take n then it closes. and when i use g or h he says: "can't do,
just y and n (for yes and no)"
but things like "*" or "%word%" don't work and yes, I have "choice /?" Already tried but when I do it like this it doesn't work either:
for %%i in (
D o " you " k n o w " to " h e l p " me
) do (<nul set /p "=.%bs%%%~i" & >nul ping -n 1 localhost)
# echo ?
CHOICE
[I don't care about syntax errors or something. if it works then it fits, it's just this one thing to use all characters, letters and symbols in one.]
No hate <3
if has no wildcard, but as I already commented, you don't need the third if at all. If Y and N are already handled, it can only be "anything else"
:start
set "input="
set /p "input=[Y / N]? "
if "%input%"=="Y" goto y
if "%input%"=="N" goto n
echo it's Y or N, nothing else&goto :start
The quotes makes it a little bit safer, but note this still isn't safe at all.
Cmd handles several characters (so called "poison chars" like <>|&"!) different (and even more under certain conditions).
A more secure method:
#echo off
setlocal enabledelayedexpansion
:loop
set "input="
set /p "input="
>nul 2>&1 (
echo/!input!|findstr /x "Y" >nul && goto YES
echo/!input!|findstr /x "N" >nul && goto NO
)
echo FAIL
goto :loop
:YES
echo yeah
goto :eof
:NO
echo nope
goto :eof
(Disclaimer: I'm quite sure, someone will find an input-string to make it fail too)
Note: both if and findstr support the /i switch to make them case-insensitive.
And just for the sake of showing everyone else how to perform this task using the appropriate commands:
%SystemRoot%\System32\choice.exe
If ErrorLevel 2 Exit /B
Rem The code you had under :end replaces this line

Regex to match a variable in Batch scripting

#echo off
SET /p var=Enter:
echo %var% | findstr /r "^[a-z]{2,3}$">nul 2>&1
if errorlevel 1 (echo does not contain) else (echo contains)
pause
I'm trying to valid a input which should contain 2 or 3 letters. But I tried all the possible answer, it only runs if error level 1 (echo does not contain).
Can someone help me please. thanks a lot.
findstr has no full REGEX Support. Especially no {Count}. You have to use a workaround:
echo %var%|findstr /r "^[a-z][a-z]$ ^[a-z][a-z][a-z]$"
which searches for ^[a-z][a-z]$ OR ^[a-z][a-z][a-z]$
(Note: there is no space between %var% and | - it would be part of the string)
Since other answers are not against findstr, howabout running cscript? This allows us to use a proper Javascript regex engine.
#echo off
SET /p var=Enter:
cscript //nologo match.js "^[a-z]{2,3}$" "%var%"
if errorlevel 1 (echo does not contain) else (echo contains)
pause
Where match.js is defined as:
if (WScript.Arguments.Count() !== 2) {
WScript.Echo("Syntax: match.js regex string");
WScript.Quit(1);
}
var rx = new RegExp(WScript.Arguments(0), "i");
var str = WScript.Arguments(1);
WScript.Quit(str.match(rx) ? 0 : 1);
Stephan's answer is correct in terms of support for regular expression. However, it does not regard a bug of findstr concerning character classes like [a-z] -- see this answer by dbenham.
To overcome this you need to specify this ( I know it looks terrible):
echo %var%|findstr /R "^[abcdefghijklmnopqrstuvwxyz][abcdefghijklmnopqrstuvwxyz]$ ^[abcdefghijklmnopqrstuvwxyz][abcdefghijklmnopqrstuvwxyz][abcdefghijklmnopqrstuvwxyz]$"
This truly matches only strings consisting of two or three lower-case letters. Using ranges [a-z] would match lower- and upper-case letters except Z.
For a complete list of bugs and features of findstr, reference this post by dbenham.
errorlevel is that number OR HIGHER.
Use following.
if errorlevel 1 if not errorlevel 2 echo It's just one.
See this
Microsoft Windows [Version 10.0.10240]
(c) 2015 Microsoft Corporation. All rights reserved.
C:\Windows\system32>if errorlevel 1 if not errorlevel 2 echo It's just one.
C:\Windows\system32>if errorlevel 0 if not errorlevel 1 echo It's just ohh.
It's just ohh.
C:\Windows\system32>
If Higher than one and not higher than n+1 (2 in this case)

Extract a dictionary of substring in bash?

I have files of the kind:
(1), (2), (3), (4), (5), (6), (10), (11), (12), (13), (14), (15), (16), (17), (18), (24), (25), (26), (27), (28), (29), (30), (31), (32), (33), (34), (35), (36), (37), (38), (39), (40), (41), (42), (43), (51), (52), (53), (54), (55), (56), (57), (58), (62), (63), (64), (65), (66), (67), (68), (69), (70), (71), (72), (73), (74) Use method number 1. (7), (8), (9), (19), (20), (21), (22), (23), (59), (60), (61) Use method number 2. (44), (45), (46), (47), (48), (49), (50) Use method number 3.
I would like to build a dictionary containing the numbers between parentheses and link them to the sentences of the type: "Use method number #". So, in this case:
1,2,3,4,5...74 --> Use method number 1.
7,8,9,19....61 --> Use method number 2.
Currently I am building a complex while that reads regexs (^ *\([0-9]+\)), extracts each number, deletes the coincidence and starts again until regex is not found and then extracts the sentence. But this is quite poor in performance and tedious to maintain.
Have you got any suggestions on how to improve this through more compact methods other than the while do?
I am not bothered by the dictionary structure, do not consider it right now if it does not imply modifying the method.
EDIT. ADDING REAL DATA STRING:
(12), (13), (14), (15) P.S.: 3 días en cultivo de invernadero. Efectuar un máximo de 6 aplicaciones por
campaña a intervalos de 7 días utilizando un volumen máximo de caldo de 600 l/Ha. y un máximo de
7,5 Kg de cobre inorgánico por campaña.
(28) Tratamiento en otoño, pulverizando hasta una altura de 1,5 m.
(44), (45), (46), (47), (48), (49), (50), (51) Efectuar sólo tratamientos desde la cosecha hasta la
floración, limitando la aplicación a 1200 l. de caldo/Ha. y un máximo de 3 aplicaciones por campaña
(con un intervalo de tratamientos de 14 días) y un máximo de 7,5 Kg. de cobre inorgánico/Ha.por
campaña.
You can use sed:
sed -r 's/( *\(|\))//g;s/\./\n/g' input.txt
This assumes that your input file does not contain line breaks. If it contains line breaks the command needs to get modified a bit.
Explanation:
The first command s/( *\(|\))//g removes the parentheses and additional whitespace. The second command s/\./\n/g adds a newline after a dot.
Oh I missed that you want to add an additional -->. If you really need that, the second sed commands needs to get modified:
sed -r 's/( *\(|\))//g;s/U[^.]+\./--> \0\n/g' input.txt
Now the second command searches for the sequence U --> until a dot and prepends a --> plus adds the newline after the dot.
Output:
1,2,3,4,5,6,10,...,74 --> Use method number 1.
7,8,9,19,20,21,22,23,59,60,61 --> Use method number 2.
44,45,46,47,48,49,50 --> Use method number 3.
One another thing: The above commands adds an additional newline at the end of output. You can suppress that by adding a third sed command s/\n$// which removes the additional new line before the end of the output:
sed -r 's/( *\(|\))//g;s/U[^.]+\./--> \0\n/g;s/\n$//' input.txt
Quite an idiomatic gnu awk solution:
awk -v RS="Use method number [0-9]."
-v OFS=" --> "
'NF{gsub(/\s*|\(|\)/, ""); print $0, RT}' file
Test
$ awk -v RS="Use method number [0-9]." -v OFS=" --> " 'NF{gsub(/\s*|\(|\)/, ""); print $0, RT}' a
1,2,3,4,5,6,10,11,12,13,14,15,16,17,18,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,51,52,53,54,55,56,57,58,62,63,64,65,66,67,68,69,70,71,72,73,74 --> Use method number 1.
7,8,9,19,20,21,22,23,59,60,61 --> Use method number 2.
44,45,46,47,48,49,50 --> Use method number 3.
Explanation
-v RS="Use method number [0-9]." set the record separator to the string "Use method number X.`, X being a digit.
-v OFS=" --> " set the print separator.
NF{gsub(/\s*|\(|\)/, ""); print $0, RT} main code
-- NF {} if there is at least one field, proceed.
-- gsub(/\s*|\(|\)/, "") remove all spaces, ( and ) from the string.
-- print $0, RT print the replaced string together with the record separator that was used ("Use method number X."). Using RT instead of RS so that we catch the value of the specific X used in the string.
From man awk:
RT
The record terminator. Gawk sets RT to the input text that matched
the character or regular expression specified by RS.
you can very intuitively do it with an ed script,
:: ed.script ::
# first you split your data in multiple lines
,s/\(\(([0-9]*), \)*([0-9]*)\)/\
\1\
/g
# then for each matching line with numbers, you remove unwanted chars
# and append " --> " to the next line
,g/\(\(([0-9]*), \)*([0-9]*)\)/\
s/[)( ]//g\
a\
-->\
.
# and finally you join lines
,g/^ -->/-1,+1j
# save if you want
w
Then you launch it with the following command:
cat ed.script | ed -s file.txt
that was the part intuitive... and it works with your sample data.

understand Regular expression in a sed command

I am learning Sed and I've been banging my head for an hour about to understand this command, here is the example from my book:
$ sed -n -e '/^[^|]*|[^|]*|56/w dpt_56' -e '/^[^|]*|[^|]*|89/w dpt_89' tel2.txt
$ cat dpt_56
Karama Josette|256 rue de la tempete|56100|Lorient|85.26.45.58
Zanouri Joel|45/48 boulevard du Gard|56100|Lorient|85/56/45/58
$ cat dpt_89
Joyeux Giselle|12. rue de la Source|89290|Vaux|45.26.28.47
Hers is what i understand:
- this command has the purpose to store in the dpt_56 file the lines of the poeple from the 56...district, ans the same for the 89 district in the dpt_89.
What I dont understand is the purpose or effect of the "|" and "^" caracters in the regex expression => What do ^[^|]*|[^|]*|56 means ? All i see is "choose every line that doesnt begin with zero or several times "|" OR that have several on no times "|"... but i get confused.
The expression [^|]*| means "any number of characters that aren't | followed by a |".
The reason [^|] is used instead of . is to ensure that the . wildcard doesn't greedily eat too much input.
It looks like the sed command itself is checking the 3rd field of a pipe delimited input. If the value starts with 56 then it writes it to dpt_56, if the value starts with 89, then it writes it to dpt_89.

Bash and regex problem : check for tokens entered into a Coke vending machine

Here is a "challenge question" I've got from Linux system programming lecture.
Any of the following strings will give you a Coke if you kick:
L = { aaaa, aab, aba, baa, bb, aaaa"a", aaaa"b", aab"a", … ab"b"a, ba"b"a, ab"bbbbbb"a, ... }
The letters shown in wrapped double quotes indicate coins that would have fallen through (but those strings are still part of the language in this example).
Exercise (a bit hard) show this is the language of a regular expression
And this is what I've got so far :
#!/usr/bin/bash
echo "A bottle of Coke costs you 40 cents"
echo -e "Please enter tokens (a = 10 cents, b = 20 cents) in a
sequence like 'abba' :\c"
read tokens
#if [ $tokens = aaaa ]||[ $tokens = aab ]||[ $tokens = bb ]
#then
# echo "Good! now a coke is yours!"
#else echo "Thanks for your money, byebye!"
if [[ $tokens =~ 'aaaa|aab|bb' ]]
then
echo "Good! now a coke is yours!"
else echo "Thanks for your money, byebye!"
fi
Sadly it doesn't work... always outputs "Thanks for your money, byebye!" I believe something is wrong with syntax... We didn't provided with any good reference book and the only instruction from the professor was to consult "anything you find useful online" and "research the problem yourself" :(
I know how could I do it in any programming language such as Java, but get it done with bash script + regex seems not "a bit hard" but in fact "too hard" for anyone with little knowledge on something advanced as "lookahead"(is this the terminology ?)
I don't know if there is a way to express the following concept in the language of regex:
Valid entry would consist of exactly one of the three components : aaaa, aab and bb, regardless of the order in a component, followed by an arbitrary sequence of a or b's
So this is what it should be like :
(a{4}Ua{2}bUb{2})(aUb)*
where the content in first braces is order irrelevant.
Thanks a lot in advance for any hints and/or tips :)
Oh, and forget about returning any money back to the user, they are all mine :)
Edit : now the code works ,thanks to Stephen has pointed out my careless typo.
you don't need a regular expression.
case $tokens in
aaaa|aab|bb) echo "coke!";;
*) echo "no coke";;
esac
note the above checks for exactly "aaaa" or exactly "aab"..if you don't care how many characters after that, use wildcard
case $tokens in
aaaa*|aab*|bb*) echo "coke!";;
*) echo "no coke";;
esac
You have:
if [[ $token =~ 'aaaa|aab|bb' ]]
$token should be $tokens
and remove the quotes from the regex.
if [[ $tokens =~ aaaa|aab|bb ]]
But, now you'll have to work on removing the quotes.