I am working on a short batch file program that accepts input and does some simple math. It seems to work correctly for everything except the first echo. Here is the code:
set /p usercommand= "Input:"
if "%usercommand%" equ "done" (
set /p usertimeouthours= "Input Hours: "
echo (%usertimeouthours%)
set /p usertimeoutminutes= "Input Minutes: "
echo (%usertimeoutminutes%)
set /p usertimeoutseconds= "Input Seconds: "
echo (%usertimeoutseconds%)
set /a answer= %usertimeouthours%*3600+%usertimeoutminutes%*60+%usertimeoutseconds%
echo %answer%
goto end
) else (
echo finished
goto user
)
:end
why does the first echo only output
(
my guess is something is wrong with my if statement, does anyone know how I should change it?
Thanks!
Problem 1 - Normal vs Delayed expansion of variables
Normal expansion using %var% occurs at parse time, and the entire IF statement is parsed all at once, including the contents within the parentheses. So something like echo (%usertimeouthours%) displays the value of USERTIMEOUTHOURS as it existed before the IF statement was executed (before SET /P set the value).
The fix is to enable delayed expansion at the top using setlocal enableDelayedExpansion and use !var! instead of %var%. Delayed expansion occurs at execution time instead of parse time.
Problem 2 - Unescaped special characters
There are a number of characters that have special meaning and must be either escaped with ^ or quoted if you want the character to be treated as a string literal.
One of the special characters is ). It will terminate any code block opened with ( unless it is escaped or quoted. You need to escape that character as ^) when you use it in your ECHO statements because those statements are within a parenthesized block of code.
Simplification of your SET /A statement
You do not need to expand variables when used in a SET /A computation. You can simply use the variable name without enclosing in percents or exclamations. This convenience only works with SET /A.
#echo off
setlocal enableDelayedExpansion
set /p usercommand= "Input:"
if "%usercommand%" equ "done" (
set /p usertimeouthours= "Input Hours: "
echo (!usertimeouthours!^)
set /p usertimeoutminutes= "Input Minutes: "
echo (!usertimeoutminutes!^)
set /p usertimeoutseconds= "Input Seconds: "
echo (!usertimeoutseconds!^)
set /a answer= usertimeouthours*3600+usertimeoutminutes*60+usertimeoutseconds
echo !answer!
goto end
) else (
echo finished
goto user
)
:end
Related
I'm trying to capitalize the first letter of the output from the %COMPUTERNAME% variable. I have tried the below code which doesn't work.
set host=%COMPUTERNAME%
echo %host% | sed 's/^\(.\)/\1\u\2/g/'
The output should be Beast rather than BEAST or beast
I ever often tend to wrap a powershell command for this:
:: Q:\Test\2018\10\12\SO_52769852.cmd
#Echo off
For /f %%A in ('
Powershell -NoP -C "$Env:COMPUTERNAME.Substring(0,1).ToUpper()+$Env:COMPUTERNAME.Substring(1).ToLower()"
') do set host=%%A
Echo:%host%
Instead of using `SEd`, you could probably do it as a single line using the built-in `Find` command.
#For /F "Tokens=2 Delims=:" %%A In ('"Find "" ":%ComputerName:~,1%" 2>&1"') Do #Echo %%A%ComputerName:~1%
The idea uses a 'quirk' with find.exe, which capitalizes the entire filename in its error message, when it cannot locate a file. I expand the %COMPUTERNAME% variable, asking for just its first character, %ComputerName:~,1%,and precede that with a character which is invalid in a Windows filename, in this case :. If we assume a %COMPUTERNAME% value of iab-desktop, the error message, (stdOut, 2>) from Find "" ":i" would be passed to the Do portion as, File not found - :I. This is the English version string, but that shouldn't matter, because we have asked for the second token delimited by the : character, which will be I. I then prepend that result, stored in %%A to the expanded value of %COMPUTERNAME%, this time asking for all characters except for its first, %ComputerName:~1%. The resulting string will be the value of %COMPTERNAME% with the first character capitalized.
try this:
#echo off
setlocal
set "f_leter=%COMPUTERNAME:~0,1%"
set "the_rest=%COMPUTERNAME:~1%"
call :UpCase %f_leter% f
call ::LoCase %the_rest% rest
set result=%f%%rest%
echo %result%
exit /b %errorlevel%
endlocal
::http://www.robvanderwoude.com/battech_convertcase.php
:LoCase
:: Subroutine to convert a variable VALUE to all lower case.
:: The argument for this subroutine is the variable NAME.
setlocal enableDelayedExpansion
set "var=%~1"
FOR %%i IN ("A=a" "B=b" "C=c" "D=d" "E=e" "F=f" "G=g" "H=h" "I=i" "J=j" "K=k" "L=l" "M=m" "N=n" "O=o" "P=p" "Q=q" "R=r" "S=s" "T=t" "U=u" "V=v" "W=w" "X=x" "Y=y" "Z=z") DO (
SET "var=!var:%%~i!"
)
endlocal&(
if "%~2" neq "" (
set "%~2=%var%"
) else (
echo %var%
)
)&GOTO:EOF
:UpCase
setlocal enableDelayedExpansion
set "var=%~1"
:: Subroutine to convert a variable VALUE to all UPPER CASE.
:: The argument for this subroutine is the variable NAME.
FOR %%i IN ("a=A" "b=B" "c=C" "d=D" "e=E" "f=F" "g=G" "h=H" "i=I" "j=J" "k=K" "l=L" "m=M" "n=N" "o=O" "p=P" "q=Q" "r=R" "s=S" "t=T" "u=U" "v=V" "w=W" "x=X" "y=Y" "z=Z") DO (
SET "var=!var:%%~i!"
)
endlocal&(
if "%~2" neq "" (
set "%~2=%var%"
) else (
echo %var%
)
)&GOTO:EOF
Here's the sed answer, although I'd recommend #LotPings powershell answer on Windows. Note that for a typical %computername% the more key point of your question is converting all but the first character to lower-case.
set host=%COMPUTERNAME%
echo %host% | sed -r 's/^(.)(.*)/\U\1\L\2/'
I try to write some simple program in windows batch file. This program is similar to some simple application from "C programming language" Kernighan and Ritchie.
The task of this program is to count char numbers and words numbers. Belove there is a source code
rem Char Number and words number
#echo off
cls
echo ===============================================================
echo input strin
set /P a=
::set variables
set Temp_str=%a%
set /A charcounter = 0
set /A wordscounter = 0
:loop
if defined Temp_str (
set /P actual_char=%Temp_str:~0,1%
if %actual_char%=="" ( set /A wordscounter+=1 )
set Temp_str=%Temp_str:~1%
set /A charcounter+=1;
goto loop
)
echo %a% %charcounter% %wordscounter%
There is error somewher but I can't find it. I don't know what is wrong. I actually start with batch windows programming.
To debug batch files always disable #echo off otherwise you're flying blind
From there you'll see the issue is on the line
if %actual_char%=="" ( set /A wordscounter+=1 )
You need to add quotes.
if "%actual_char%"=="" ( set /A wordscounter+=1 )
In addition there are other issues with the code:
1).
set /P actual_char=%Temp_str:~0,1%
Should not have the /p option as you're not wanting to prompt the user to enter the character.
2) Your word counter should be checking for spaces in addition to the blank string and you also should check what happens with the end of the first word.
rem Char Number and words number
rem #echo off
cls
echo ===============================================================
set /P a=input string:
::set variables
set Temp_str=%a%
set /A charcounter = 0
set /A wordscounter = 0
:loop
if "%Temp_str%" NEQ "" (
set actual_char=%Temp_str:~0,1%
if "%actual_char%"==" " ( set /A wordscounter+=1 )
set Temp_str=%Temp_str:~1%
set /A charcounter+=1;
goto loop
)
REM Increment word count for the final word
if "%a%" NEQ "" ( set /A wordscounter+=1 )
echo %a% %charcounter% %wordscounter%
I am having trouble splitting a line which contains double quoted strings separated by comma. String looks something like:
"DevLoc","/Root/Docs/srvr/temp test","171.118.108.22","/Results/data/Procesos Batch","C:\DataExport\ExportTool\Winsock Folder DB","C:\Export\ExportTool\Temp Folder","22"
Some strings values contain spaces. I want to store each double quoted string into a variable. Can anyone please help
Below is my batch script. variable 'EnvDetails' contains above line which need to be parsed.
FOR /F "tokens=1,2,3,4,5,6,7 delims=," %%i in ("%EnvDetails%") do (
SET TEMPS=%%i
SET Path=%%j
SET host=%%k
SET scriptPath=%%l
SET WINSP_HOME=%%m
SET PUTTY_HOME=%%n
SET portNum=%%o
#echo %TEMPS% > temp.txt
#echo %MPath% >> temp.txt
#echo %host% >> temp.txt
#echo %scriptPath% >> temp.txt
#echo %WINSCP_HOME% >> temp.txt
#echo %PUTTY_HOME% >> temp.txt
#echo %portNum% >> temp.txt
)
Part of the problem is that you're attempting to retrieve your variable values within the same parenthetical code block as they're set. Because the cmd interpreter replaces variables with their values before the commands are executed, you're basically echoing empty values to temp.txt. To wait until the variables have been defined before expanding them, you'd need delayed expansion.
But you're really making this more complicated than it needs to be. What else are you doing with the variables, besides echoing them out to a text file?
What you should do instead is use a basic for loop rather than for /f. for without any switches evaluates lines similar to CSV parsers anyway, splitting on commas, semicolons, unquoted spaces and tabs, and so forth.
Given that you're basically splitting a line on commas and echoing each token, in order, to a text file, one token per line, you can simplify your code quite a bit like this:
#echo off
>temp.txt (
for %%I in (%EnvDetails%) do echo %%~I
)
If I'm mistaken and you do indeed intend to perform further processing on the data; if you do actually need the variables, then this example demonstrates delayed expansion:
#echo off
setlocal
set EnvDetails="DevLoc","/Root/Docs/srvr/temp test","171.118.108.22","/Results/data/Procesos Batch","C:\DataExport\ExportTool\Winsock Folder DB","C:\Export\ExportTool\Temp Folder","22"
>temp.txt (
FOR /F "tokens=1-7 delims=," %%i in ("%EnvDetails%") do (
SET "TEMPS=%%~i"
SET "MPath=%%~j"
SET "host=%%~k"
SET "scriptPath=%%~l"
SET "WINSCP_HOME=%%~m"
SET "PUTTY_HOME=%%~n"
SET "portNum=%%~o"
setlocal enabledelayedexpansion
echo !TEMPS!
echo !MPath!
echo !host!
echo !scriptPath!
echo !WINSCP_HOME!
echo !PUTTY_HOME!
echo !portNum!
endlocal
)
)
Final note: The tilde notation of %%~i, %%~j, etc, strips surrounding quotation marks from each token. If you intentionally wish to preserve the quotation marks as part of the variable values, remove the tildes.
I have a log file I need to process and extract data from. Each line contains a string of an event log output. Unfortunately, the parts of the string is NOT uniformly formatted. Here are a few example lines:
"Some random length string. 0x8dda46 0x1 0x384 C:\Program Files (x86)\some\path\foo0.exe "
"Some random leeeength string. 0xa95ac2 0x8cc C:\Program Files (x86)\some\path\foo1.exe %%1936 0xcc0 "
"Some random leength string. 0xbcd668 0x330 C:\Program Files (x86)\some\path\foo2.exe %%1936 0xf38 "
"Some random leeeeeeeength string. 0xbcd668 0x1 0x330 C:\Program Files (x86)\some\path\foo2.exe "
"Some random leeength string. 0x352c44 0xfc0 C:\Program Files (x86)\some\path\foo3.exe %%1936 0x92c "
"Some random leeeeength string. 0xa95ac2 0x0 0x8cc C:\Program Files (x86)\some\path\foo1.exe "
"Some random leength string. 0x352c44 0x0 0xfc0 C:\Program Files (x86)\some\path\foo3.exe "
I need to extract the "foo.exe" file name without the full path and the HEX value just before the "C:\Progra..." (it's the process ID)
so I want the output be:
0x384 foo0.exe
0x8cc foo1.exe
0x330 foo2.exe
0x330 foo2.exe
0xfc0 foo3.exe
0x8cc foo1.exe
0xfc0 foo3.exe
I'm trying to achieve the goal with as less "hard coded" search/replace as possible since many parts of the string is not going to be the same content or same length. I tried to use FOR /F to split the string, but I have no way to locate the two columns as they are always changing. Only thing is constant is the "C:\Program Files (x86)" part. (Plus FOR has a 52 variable limit)
I have written some tricky batch files, but I'm starting to think I'm asking too much of DOS ;-)
Thanks in advance for any help!
#ECHO OFF
SETLOCAL
FOR /f "tokens=1*delims=." %%a IN (q28333414.txt) DO (
FOR /f "tokens=1*delims=:" %%c IN ("%%~b") DO CALL :process %%c&CALL :report "%%d
)
GOTO :EOF
:process
SET hexval=%~3
IF DEFINED hexval shift&GOTO process
SET "hexval=%~1"
SET "drive=%~2:"
GOTO :eof
:report
SET "line=%drive%%~1"
SET "line="%line:.exe=.exe"%"
FOR %%r IN (%line%) DO ECHO %hexval% %%~nxr&GOTO :eof
I used a file named q28333414.txt containing your data for my testing.
The first process simply throws away each (space-delimited) parameter between the . and : until there are exactly two left - the required hexval and the drive letter.
The report process re-attaches the drive letter and encloses it and the .exe name in quotes. the for %%r picks the first string, shucks off the quotes, spits out the result and all's done.
Edit : fixed report to show name and extension of file only as required and dbenham comment
Breaking news: (literally!)
#ECHO OFF
SETLOCAL enabledelayedexpansion
FOR /f "delims=" %%a IN (q28333414.txt) DO SET "line=%%~a"&CALL :process "!line::=" "!"
)
GOTO :EOF
:process
SET "hexval=%~3"
IF DEFINED hexval shift&GOTO process
CALL :lastbar1 %%~1
SET "filename=%~2"
SET filename="c:%filename:.exe =.exe" %
FOR %%r IN (%filename%) DO ECHO %hexval% %%~nxr&GOTO :eof
GOTO :eof
:lastbar1
SET "hexval=%~3"
IF DEFINED hexval shift&GOTO lastbar1
SET "hexval=%~1"
GOTO :eof
OK - let's try this, then.
For each line, replace all evil colons with " " and pass resultant quoted-string sequence to a subroutine.
Shift the parameters until there are but 2, which will be the string before and after the final countdown - er, colon.
Repeat the process for the first parameter. The penultimate value is the required hexval.
with the second parameter, add "c: before and " after any .exe, so the result is a quoted full-filename and dross; spit out the hexval and filename and done...
small revision in the rather dim light of the "&" comment - the famous set "var=whatever" formula fails with & included in this case (as in subdirectory "Documents & Settings") so the enclosing quotes can be removed as trailing spaces are not relevant. Would have been useful to know what the test data triggering the problem was though - reduces guesswork.
Any good regex utility you can lay your hands on should be able to solve your problem. I like to use my JREPL.BAT hybrid JScript/batch utility. It is pure script that runs natively on any Windows machine from XP onward.
Assuming your file is test.log, then I would use:
jrepl ".* (0x[0-9A-F]+) C:\\Program Files \(x86\)\\(?:.*\\)?([^\\]+\.exe) .*" "$1 $2" /i /f test.log
On each line it looks for the last occurrence of a hex string sandwiched by spaces that precedes a file path that begins with "C:\Program Files (x86)\" and ends with ".exe". I made the search ignore case.
This solution assumes that there are not backslashes into the random string.
#echo off
setlocal EnableDelayedExpansion
for /F "tokens=1-5 delims=\" %%a in (logFile.txt) do (
rem Extract the HEX value
for %%A in (%%~a) do (
set "value=!lastButOne!"
set "lastButOne=%%A"
)
rem Extract the file name
for /F %%A in ("%%e") do set "name=%%A"
echo !value! !name!
)
Here's a hybrid batch + JScript script (but still a .bat file) that will perform a regexp replace similar to NextInLine's PowerShell solution.
#if (#CodeSection == #Batch) #then
#echo off
setlocal
set "logfile=test.log"
rem // Ask JScript to parse log. On each line, %%I = hex. %%J = exe.
for /f "tokens=1*" %%I in ('cscript /nologo /e:JScript "%~f0" "%logfile%"') do (
echo %%I %%J
)
rem // End main runtime.
goto :EOF
#end
// JScript chimera portion
var fso = WSH.CreateObject('Scripting.FileSystemObject'),
log = fso.OpenTextFile(WSH.Arguments(0), 1);
while (!log.AtEndOfStream) {
var line = log.ReadLine();
WSH.Echo(line.replace(/^.+(0x[0-9a-f]+) \w:\\.+?\\(\w+\.exe).+$/i, "$1 $2"));
}
log.Close();
Course if I were in your boat I'd probably use GnuWin32 sed.
sed -r -e "s/^.*(0x[a-f0-9]+) \w:.+\\(.+\.exe).*$/\1 \2/i" test.log
Just for giggles, I ran some time tests of each fully-working solution against the O.P.'s test log file above, running each several times and getting the mode duration (the result occurring most often).
Aacini's solution: 0.013s (Excellent, but depends on narrow matches)
sed: 0.015s (simplest)
Magoo's solution: 0.034s (clever!)
my JScript hybrid: 0.034s (the best, of course)
dbenham's jrepl.bat: 0.051s (powerful Swiss army knife solution)
NextInLine's PowerShell: hanged my timer script, but felt like about a half a second after the initial painful priming of PowerShell
This is really a task that calls for regular expressions, and for regular expressions at the windows command-line you want powershell. Fortunately, you can run powershell from a batch file or the DOS command-prompt:
powershell -Command "(Get-Content 'c:\full_path_here\input.log') -replace '.+?(0x[0-9a-f]{3}) .+?\\([^\\]+\.exe).*', '$1 $2'"
This has a few parts
powershell -Command runs the entire expression in quotation marks as though it were run from the powershell command line
Get-Content is like the linux cat command - it reads the entirety of the file contents
-replace uses regular expressions to replace the content on each line of the file with the two matched expressions in parentheses
i want to use my %variable% to manage the conditional clauses in a IF.. THEN.. ELSE in a batch file.
Something like the following:
set variable=%%homedrive%% EQU C:
if %variable% (
echo test ok
) else (
echo test fail
)
if i write on a cmd console:
set test=1 equ 1
if %test% echo OK
it works!
i'll use it in a for /f cicle:
this is my pseudo codethis is my pseudo code to correct
(
rem echo "%systemdrive%;;"
echo "%%COMPUTERNAME%% EQU [x];[some parameters1]"
echo "%%USERNAME%% NEQ [y];[some parameters2]"
echo "%%LOGONSERVER%% EQU [z];[some parameters3]"
[..]
) > "%temp%\CSG_fs.tmp"
[..]
for /f "usebackq tokens=1-2* delims=;" %%a in ("%temp%\CSG_fs.tmp") do (
set cond=%%a& set cond=!cond:~1!
set parm=%%b& set parm=!parm:~0,-1!
echo - cicle: "!cond!" --^> "!parm!"
call if !cond! call:CSG_sub_fs !parm!
echo - done
)
goto:eof
:CSG_sub_fs
[..]
goto:eof
--edit--
how can i use the variable !cond! to decide if execute the call to CSG_sub_fs?
call if !cond! call:CSG_sub_fs !parm!
does not work because it returns: "Can not find the batch label specified - IF"
and if i use
if !cond! call:CSG_sub_fs !parm!
it will say: "call:CSG_sub_fs not expected"
Well - there doesn't seem to be a question, so it's not that easy to answer.
You have a problem with
echo "^%COMPUTERNAME^% EQU [x];[some parameters1]"
because ^ does not escape % - % escapes % - use %%COMPUTERNAME%%...
(you should have been able to check this just by TYPEing "%temp%\CSG_fs.tmp"
Next problem is that
for /f "tokens=1-2* delims=; usebackq" %%a in (%temp%\CSG_fs.tmp) do (
may process the file %temp%\CSG_fs.tmp provided %temp%\CSG_fs.tmp contains no spaces, semicolons or commas. If it contains any of these deafult separators, or certain other characters with a special meaning, then you must enclose the filename in double-quotes "%temp%\CSG_fs.tmp"and use the usebackq option.
You've attempted to use usebackq but DELIMS must be the LAST option if it is used. Your code would set ";","","u","s","e","b","a","c","k" and "q" as delimiters.
Beyond that, perhaps if you explain what you intend to achieve, we'd be able to devise the appropriate code.
Try this:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
CLS
REM I'm setting these variables for testing.
REM That isn't ususally a good idea but the SETLOCAL
REM will ensure they are restored on exit
SET computername=[x]
SET logonserver=[z]
(
rem echo "%systemdrive%;;"
echo "%%COMPUTERNAME%% EQU [x];[some parameters1]"
echo "%%USERNAME%% NEQ [y];[some parameters2]"
echo "%%LOGONSERVER%% EQU [z];[some parameters3]"
) > "%temp%\CSG_fs.tmp"
for /f "usebackqtokens=1-2* delims=;" %%a in ("%temp%\CSG_fs.tmp") do (
set cond=%%a& set "cond=IF !cond:~1! CALL :csg_sub_fs "
set parm=%%b& set parm=!parm:~0,-1!
CALL :varcmd "!cond!" "!parm!"
)
GOTO :eof
:varcmd
%~1 %~2
GOTO :eof
:csg_sub_fs
ECHO parameters supplied to csg_sub_fs were: %*
GOTO :eof
I've forced the variablenames to match the conditions you've used in order to trigger the subroutine calls. Change as you need to prove your concept.
And dont worry about imperfect English. I'm sure I wouldn't do as well in your language!