Error ""tmp.txt" was unexpected at this time" - loops

I am still pretty new to batch and im running into this issue that i cannot seem to solve. When I run my script it returns "6766.txt" was unexpected at this time.My script is supposed to search for "store_versions" in a file called Local State. If it finds that line it it will be appended to a Temp file and then trim the line in a loop so that i only get A specific part of the output of the find command. The output of the command is [319] "store_versions": { . And all I want is 319. Does anyone know what is causing this issue?
Thanks,
#echo off
setlocal enableextensions enabledelayedexpansion
set tempfile="%random%.txt"
set LineNumFin=%LineNumFin%
pause
Find /N "store_versions" < "Local State" > %tempfile%
for /f "tokens=1 delims=[]" %%a in %tempfile% do ( set LineNum=%%a
pause
)
del "%tempfile%"
echo Line Num: %LineNum%
set /a result=%LineNum%+4
echo %result%

The in clause of the for command needs parenthesis
for /f "... options ..." %%a in ( file ) do ....
^ ^
^ here ^
And, as you are including quotes in the name of the file, you will need to include usebackq in the options part of the for command
Or, you can avoid the temporary file
for /f "tokens=1 delims=[]" %%a in (
' find /n "store_versions" ^< "Local State" '
) do set "lineNum=%%a"
The for command will execute the command, tokenize the records and retrieve the value.

Related

For loop in batch file with spaces and more than 1 token

I'm trying to run a batch file with this code:
SET /a rotateval = 0
FOR /F "tokens=1,2" %%A IN ('C:\Users\Max\Google Drive\Misc\MaxPDFcompress\ImageMagick-7.0.9-7-portable-Q16-x64\convert "%%f" -format "%%[w] %%[h]" info:') DO (
IF %%A GTR %%B SET /a rotateval = 90
I'm getting the error that
'C:\Users\Max\Google' is not recognized as an internal or external command,
operable program or batch file.
I tried different combos of "delim=" and "usebackq" but to no avail.
The error you are reporting is double-quoting related in the parenthesized command within your FOR /F loop.
Your submitted content should look like this:
Set "rotateval=0"
For /F "Tokens=1,2" %%A In ('^""C:\Users\Max\Google Drive\Misc\MaxPDFcompress\ImageMagick-7.0.9-7-portable-Q16-x64\convert.exe" "%%f" -format "%%[w] %%[h]" info:^"') Do (
If %%A Gtr %%B Set "rotateval=90"
The parenthesized command is passed to another cmd.exe instance, and in doing so, it removes leading and closing double-quotes. To prevent that just place the entire command within escaped doublequotes, which when removed will leave your command as needed:
"C:\Users\Max\Google Drive\Misc\MaxPDFcompress\ImageMagick-7.0.9-7-portable-Q16-x64\convert.exe" "%f" -format "%[w] %[h]" info:

Supress all output in this batch file

I have the following batch file which outputs limited info. How do I suppress all output from this batch file? What I'd like to see is after I enter the name of the batch file and hit enter, the next thing that shows up on screen is "C:>".
#ECHO OFF
SETLOCAL EnableDelayedExpansion
(for /f "tokens=1 delims=;" %%A in (C:\ALL.txt) do (
#echo %%A | find /i "\"
if errorlevel 1 (
DEL "D:\!mypath!%%A" >> C:\ALL-OUT.txt 2>&1
) ELSE (
set mypath=%%A
)))
#endlocal
I think it all boils down to this line which I use to test whether %%A contains backslash:
#echo %%A | find /i "\"
I tried adding " 1>null" to the end of the line. It worked to suppress all output. However, it also created a file named "null".
Since "For /f" parses files line by line, I wonder if there is a way to incorporate the echo command in "For /f"?
The NUL device is referenced with nul, not null. >null creates a file named null.
for your second question: you can redirect the output of a command block with a single redirection (in fact, this is way faster)
#ECHO OFF
SETLOCAL EnableDelayedExpansion
(
for /f "delims=;" %%A in (C:\ALL.txt) do (
echo %%A | find "\" >nul
if errorlevel 1 (
DEL "D:\!mypath!%%A"
) ELSE (
set mypath=%%A
)
)
) > C:\ALL-OUT.txt 2>&1
endlocal

Batch File: Assign random line of text file as variable for later use

I'm trying to write a very simple batch file for personal use...It's complete except for one thing I'm stumped on. Hopefully this is an easy fix (I'm effectively illiterate when it comes to code).
Basically what I'm trying to do is have the script choose a random line from a text file, do this a couple times with a couple different text files, then I wish to assign the output from each text file to a variable so that I can easily use them in various combinations...then repeat the process.
Here is what I have right now...
#ECHO OFF
:START
SETLOCAL
SETLOCAL EnableDelayedExpansion EnableExtensions
SET "list1=list1.txt"
FOR /f %%a IN ('type "%list1%"^|find /c /v ""') DO SET /a numlines=%%a
SET /A list1random=(%RANDOM% %% %NumLines%)
IF "%list1random%"=="0" (SET "list1random=") ELSE (SET "list1random=skip=%list1random%")
FOR /F "usebackq tokens=* %list1random% delims=" %%A IN (`TYPE %list1%`) DO (
>> output.txt ECHO %%A
)
:Finish
ENDLOCAL
GOTO START`
This procures the random line, and spits it to a text file. All is well, next step, take that random result and assign it to a variable...
#ECHO OFF
:START
SETLOCAL
SETLOCAL EnableDelayedExpansion EnableExtensions
SET "list1=list1.txt"
FOR /f %%a IN ('type "%list1%"^|find /c /v ""') DO SET /a numlines=%%a
SET /A list1random=(%RANDOM% %% %NumLines%)
IF "%list1random%"=="0" (SET "list1random=") ELSE (SET "list1random=skip=%list1random%")
FOR /F "usebackq tokens=* %list1random% delims=" %%A IN (`TYPE %list1%`) DO (
SET output1=%%A
)
>> output.txt ECHO %output1%
:Finish
ENDLOCAL
GOTO START
Now the output ceases to be random...instead it is always the last line of the referenced text file.
EDIT: The site suggested another question that was similar to mine. However, that person was having trouble getting the script to choose a valid line. I get a valid line every time, and a random one too (when I check it via echo), but a non-random line when proceeding on, assigning the output to a variable. I don't understand because it seems like a post-facto derandomization. I.E. the difference between the two scripts has nothing to do with procuring the random result, only what to do with that result AFTER it has it, right?
I appreciate any help in advance, this is the last step before I know everything I need to finish this, I'm excited!
Sorry, you're right...anyways, I figured out a simple workaround, probably not the quickest in terms of processing time, but whatever. Basically allow the initial part of the script to spit out the random result to a text file (as seemed to work just fine) then reference the text file as a variable.
#ECHO OFF
:START
SET "list1=list1.txt"
FOR /f %%a IN ('type "%list1%"^|find /c /v ""') DO SET /a numlines=%%a
SET /A listchoice=(%RANDOM% %% %NumLines%)
IF "%listchoice%"=="0" (SET "listchoice=") ELSE (SET "listchoice=skip=%listchoice%")
FOR /F "usebackq tokens=* %listchoice% delims=" %%A IN (`TYPE %list1%`) DO (
>> listoutput.txt ECHO %%A
)
Set /p list=<listoutput.txt
>> result.txt ECHO %list%
:Finish
DEL listoutput.txt
GOTO START
This is easy to do in PowerShell using the built-in Get-Random cmdlet.
$line = (Get-Content file.txt | where { $_ } | Get-Random)
Which makes it also easy in batch.
set filename=file.txt
for /f "tokens=*" %%a in ('powershell -ex bypass -c "gc %filename% | ? { $_ } | Get-Random"') do (
set "var=%%a"
)
The where { $_ } clause is only necessary to filter out any blank lines. You can omit it if you know your file has none.

Read the first word on a specific line from a text file using batch script then feed into an if statement

Sorry I know similar things have been asked here but basically I'm trying to read a text file within a batch script and evaluate what has been written to the file.
The job is a print job that sends a file to a printer, I have it echoing the output from the command to a log file. I then want to read in what the output was and if there was an error I will then send an email so we know when things stop working.
It always appends to the end of the file so I know if there's an error the 4th from last line will begin with "Error:". So my question is how can I read that in to a variable so I can perform an IF statement. I've got the emailing part sorted it's just reading from the file that I'm struggling with.
Any help would be much appreciated. Here's an example of the content of the file when there's an error:
----
C:\XG1\DGS01\prints\000000398200001.XG1
19/03/2013
15:02
1 file(s) copied.
Error: print server unreachable or specified printer does not exist.
1 file(s) moved.
It leaves one blank line at the end of the file so I'm going with the last line minus 4.
Thank you
as a line in your batch file:
for /f "tokens=1*delims=:" %%i in (thenameofyourfile) do if "%%i"=="Error" set message=%%j
echo message was "%message%"
Actually, that will report if ANY lines are in the format you describe.
#ECHO OFF
SETLOCAL
(SET message=)
FOR /f "tokens=1,2*delims=[]:" %%i IN (
' TYPE thenameofyourfile^|find /n /v "" '
) DO (
SET lastline=%%i
IF "%%j"=="Error" SET errorline=%%i&SET message=%%k
)
SET /a target=%errorline% + 3
IF %target% neq %lastline% (SET message=)
IF DEFINED message ECHO error found %message%
should get the line ONLY if it's the fourth last line in the file - the "+ 3" being the line-count required (well, minus 1)
BUT - remember that if this is, as it seems, a log file that it's possible (I'd imagine) that further entries may appear AFTER the error (for further jobs) so the target for the Line beginning "Error:" may not be the fourth-last...
OTOH, using the line(s) I first posted, once an "Error:..." line appears, it will be detected EVERY time - you'd need to reset the logfile in your mail-send procedure (save existing & recreate empty?)
Solution without any loop (does test if any error):
#findstr Error: printer.log >nul 2>&1
#if %errorlevel% equ 0 echo Send email now!
And this code does only test the fourth line before the last line for an error:
#echo off &setlocal enabledelayedexpansion
for /f %%i in ('findstr /n "^" printer.log') do (
set line4=!line3!&set line3=!line2!&set line2=!line1!&set line1=%%i)
echo %line4%|findstr Error: >nul 2>&1
if %errorlevel% equ 0 echo Send email now!
If you want to start reading the fourth line from the bottom of your log file, you can count the number of lines in your log file, subtract 4, then more +%count% to get the tail of the log.
#echo off
setlocal
set logfile=printerlog.log
for /f "tokens=2 delims=:" %%I in ('find /c /v "" "%logfile%"') do set lines=%%I
set /a "tail4=%lines% - 4"
for /f %%I in ('more +%tail4% "%logfile%" ^| find /i "Error:"') do (
rem do your email voodoo here
)
If your log file will exceed 65,535 lines, I recommend looping through the file using JScript or VBScript instead of a batch loop. Skipping that many lines with more will cause more to crash, so you'd have to loop line-by-line and increment a counter. However, batch for loops are terribly slow.
Here's the same script that replaces more with JScript-ish file reading.
#if (#a==#b) #end /*
:: batch portion
#echo off
setlocal
set logfile=printerlog.log
for /f "tokens=2 delims=:" %%I in ('find /c /v "" "%logfile%"') do set lines=%%I
set /a "tail4=%lines% - 4"
for /f "delims=" %%I in ('cscript /nologo /e:jscript "%~f0" %tail4% "%logfile%" ^| find /i "Error:"') do (
rem do your email voodoo here
rem %%I contains the matching line.
rem After one match, stop processing further
goto :EOF
)
goto :EOF
::JScript portion */
var i=0, fso = new ActiveXObject("Scripting.FileSystemObject").OpenTextFile(WSH.Arguments(1),1);
while (!fso.AtEndOfStream) {
if (i++ < WSH.Arguments(0)) fso.SkipLine();
else WSH.Echo(fso.ReadLine());
}
fso.Close();
I believe the fastest and least resource-intensive way to check for errors is with GNU tail. If you can, get the .zip binaries and put tail.exe in your path or where your batch script can access it. Then:
#echo off
setlocal
tail -n 4 printerlog.log | find /i "Error:" >NUL && (
echo Error found. Sending email.
rem do email stuff
)
Or if you wish to capture the text of the error for your email:
#echo off
setlocal
for /f "delims=" %%I in ('tail -n 4 printerlog.log ^| find /i "Error:"') do (
echo Error found: %%I
rem do email stuff
goto :EOF
)
tail is much more efficient than all these other methods of counting the number of lines in the log file and looping through the log file line-by-line, whether looping in batch or in JScript.

Batch - Combining Two Pieces of Text on One Line in a Loop

I'm trying to work up a batch file to combine two pieces of text on one line. The first is the filename; the second is the first line of text beginning with "To: ". I have been struggling for hours and this is as far as I've gotten:
#echo off
IF EXIST fullnames.txt DEL fullnames.txt
FOR /F %%g IN ('dir /b *.eml') DO (
SET filename=%~f1
SET toline=FINDSTR /B /C "To: "
ECHO %FILENAME%%TOLINE% >> fullnames.txt
)
and it doesn't work. I am getting errors or incorrect results almost regardless of what I put down for the filename line; haven't even begun to test the toline part. Any suggestions?
You already used FOR /F to capture the output of the DIR command. Capturing the output of FINDSTR is no different.
However, it is more efficient to use a simple FOR in place of the FOR /F with the DIR command.
You used %~f1 when I think you intended %%~fg.
You cannot expand a variable set within parentheses using %var%, you must use !var! delayed expansion instead. Type SET /? from the command line for more information - read the section starting with "Finally, support for delayed environment variable expansion has been
added..."
However, in your case, you can easily avoid using delayed expansion (not that it is a problem).
Instead of deleting any existing "fullnames.txt" and then appending output to it, it is more efficient to enclose the entire construct within parentheses and redirect all output to the file using the over-write mode.
#echo off
(
for %%F in (*.eml) do (
for "delims=" %%A in ('findstr /b /c:"To: " "%%F"') do echo %%F %%A
)
) >fullnames.txt
But the above solution, simple as it is, is much more complicated than it needs to be.
FINDSTR can process multiple files specified with wildcards, and it will prefix each matching line with the filename followed by a colon.
You can get your results simply from the command line without even using a batch file (or you could put this in a batch file):
findstr /b /c:"To: " *.eml >fullnames.txt
Edit
If you are concerned that a file might contain multiple lines starting with "To: ", and you only want to use the first line, then it is back to using a batch file:
#echo off
setlocal enableDelayedExpansion
set "prevFile="
(
for /f "tokens=1* delims=:" %%A in ('findstr /b /c:"To: " *.eml') do (
if "%%A" neq "!prevFile!" echo %%A: %%B
set "prevFile=%%A"
)
) >fullnames.txt
The above solution could fail if a filename contains !. Also, a path could be used with *.eml as long as the path does not contain a drive letter. Both the drive and ! issues can be resolved with additional modifications.
#echo off
setlocal EnableDelayedExpansion
if exist fullnames.txt del fullnames.txt
for %%f in (*.eml) do (
set toline=
for /F "delims=" %%l in ('findstr /B /C:"To: " "%%f"') do (
if not defined toline set "toline=%%l"
)
echo %%f!toline! >> fullnames.txt
)
EDIT: Simpler method added
The set toline= command delete 'toline' variable before each file is processed, so just the first "To: " matching line found is assigned to it and later shown using Delayed Expansion. However, this process may be achieved in a simpler way that doesn't require Delayed Expansion, as dbenham suggested:
#echo off
if exist fullnames.txt del fullnames.txt
for %%f in (*.eml) do (
set firstFind=
for /F "delims=" %%l in ('findstr /B /C:"To: " "%%f"') do (
if not defined firstFind set firstFind=now & echo %%f%%l >> fullnames.txt
)
)
You can't assign and use environment variables inside a for loop. Use delayed variable expansion or call a subroutine.
Delayed would look something like this
setlocal EnableDelayedExpansion
#echo off
IF EXIST fullnames.txt DEL fullnames.txt
FOR /F %%g IN ('dir /b *.eml') DO (
SET filename=%~f1
SET toline=FINDSTR /B /C "To: "
ECHO !FILENAME!!TOLINE! >> fullnames.txt
)
However, that doesn't look like it would work correctly anyway. I would do it like this
FOR /F %%g IN ('dir /b *.eml') DO call :process %%g
goto :eof
:process
SET filename=%~f1
SET toline=FINDSTR /B /C "To: "
ECHO %FILENAME%%TOLINE% >> fullnames.txt

Resources