3

I made a batch file that displays the names of all the videos available in the current folder and asks you which one to play. Then based on your choice it plays that particular video and logs that video's name into the file "watched.txt". So, basically I want "watched.txt" to keep a record of all the watched video names in decreasing order of episode numbers. If a video was watched previously it should not be logged again into the file watched.txt.

Also, all the video names has the same structure as "Suits.S03Exx.720p" where only xx (i.e, the episode number) changes. xx is always a two digit integer (like 01, 02, 03, ...,11, ...).

The batch script (provided at last) reads the file "watched.txt" line by line using a for loop.

Below are the contents of the file "watched.txt" for now:

Suits.S03E10.720p                       
Suits.S03E09.720p                        
Suits.S03E08.720p                         
Suits.S03E07.720p                          
Suits.S03E06.720p                           
Suits.S03E05.720p                            
Suits.S03E04.720p                             
Suits.S03E03.720p                              
Suits.S03E02.720p                               
Suits.S03E01.720p

Now, when I play episode no: 08 through my batch file (file name: "Suits.S03E08.720p") for the second time, it is being logged again into watched.txt which is not what I want. It produces below "watched.txt" file:

Suits.S03E08.720p 
Suits.S03E10.720p                          
Suits.S03E09.720p                           
Suits.S03E08.720p                            
Suits.S03E07.720p                             
Suits.S03E06.720p                              
Suits.S03E05.720p                               
Suits.S03E04.720p                                
Suits.S03E03.720p                                 
Suits.S03E02.720p                                  
Suits.S03E01.720p

However, I was expecting the same watched.txt with no change in its contents as episode no 08 was played earlier and is already present in it.

I inspected the temporary files (temp1.txt, temp2.txt & temp3.txt) used in the manipulation of watched.txt (by commenting out these statements from the batch file for testing purposes: del temp1.txt, del temp2.txt, del temp3.txt)

The first temporary file temp1.txt was found empty. Instead, I was expecting it to be having below contents as per the logic of my batch file:

Suits.S03E10.720p                       
Suits.S03E09.720p 

The second temporary file temp2.txt was found to be having below contents:

Suits.S03E10.720p                        
Suits.S03E09.720p                         
Suits.S03E08.720p                          
Suits.S03E07.720p                           
Suits.S03E06.720p                            
Suits.S03E05.720p                             
Suits.S03E04.720p                              
Suits.S03E03.720p                               
Suits.S03E02.720p                                
Suits.S03E01.720p

Instead, I was expecting temp2.txt to be empty as per the logic of my batch file.

Now, the third temporary file temp3.txt was found to be having below contents:

Suits.S03E08.720p 
Suits.S03E10.720p                        
Suits.S03E09.720p                         
Suits.S03E08.720p                          
Suits.S03E07.720p                           
Suits.S03E06.720p                            
Suits.S03E05.720p                             
Suits.S03E04.720p                              
Suits.S03E03.720p                               
Suits.S03E02.720p                                
Suits.S03E01.720p

Instead, I expected temp3.txt to be empty as per the logic of my batch file.

Below is the end portion of the Batch file (I have not attached full code) I'm using with these inputs justPlayedName=Suits.S03E08.720p & justPlayedNumber=08 (these inputs are coming from initial portions of the full batch file):


break> temp1.txt
break> temp2.txt
break> temp3.txt

setlocal ENABLEDELAYEDEXPANSION 
for /f "tokens=*" %%S in (watched.txt) do (
    set watchedName=%%S
    set watchedNumber= !watchedName:~10,2!
    if !watchedNumber! EQU !justPlayedNumber! ( goto EOFL_2 ) 
    if !watchedNumber! GTR !justPlayedNumber! (
        echo !watchedName! >> temp1.txt
    )
    if !watchedNumber! LSS !justPlayedNumber! (
        echo !watchedName! >> temp2.txt
    )
)
endlocal

type temp1.txt > temp3.txt
echo %justPlayedName% >> temp3.txt
type temp2.txt >> temp3.txt
type temp3.txt > watched.txt
:EOFL_2
del temp1.txt
del temp2.txt
del temp3.txt

pause

Someone, please tell me where is the issue?

mzn
  • 47
  • 1
  • 5

2 Answers2

3

There are several things wrong in your code.

set watchedNumber= !watchedName:~10,2!

The space after equal sign is also part of the string value assigned to environment variable watchedNumber. See also: Why is no string output with 'echo %var%' after using 'set var = text' on command line?

if !watchedNumber! EQU !justPlayedNumber!

The operator EQU results in comparing the two strings with conversion to 32-bit signed integer values if that is possible at all. The issue here is that a string like 08 or 09 is interpreted as invalid octal integer number and not as decimal number 8 or 9 with a leading 0. For that reason this integer comparison does not work as expected by you at least for the string values 08 or 09. See also: Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files. The referenced answer explains very detailed how command IF makes a comparison on using operator EQU in comparison to using operator ==.

The entire code is more complex than necessary. There are two executables installed by default on Windows to find strings in a file: FIND and FINDSTR. Both can be used to search for a string like Suits.S03E08.720p in file watched.txt depending on string value of justPlayedNumber. Both executables exit with 1 if the searched string could not be found in the file.

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "justPlayedNumber=08"
if not "%~1" == "" (
    for /F delims^=0123456789^ eol^= %%I in ("%~1") do (
        echo "%~1" is not a valid number!
        goto EndBatch
    )
    set "justPlayedNumber=%~1"
)
if "%justPlayedNumber:~1%" == "" set "justPlayedNumber=0%justPlayedNumber%"
set "justPlayedNumber=%justPlayedNumber:~0,2%"

%SystemRoot%\System32\findstr.exe /B /L /C:"Suits.S03E%justPlayedNumber%.720p" "%~dp0watched.txt" >nul 2>nul
if errorlevel 1 (
    echo Suits.S03E%justPlayedNumber%.720p>>"%~dp0watched.txt"
    %SystemRoot%\System32\sort.exe "%~dp0watched.txt" /O "%~dp0watched.txt"
    echo Added Suits.S03E%justPlayedNumber%.720p to file watched.txt.
)

:EndBatch
endlocal

The first lines up to first empty line are just for making it easy to test the next six lines which are for this task. Those lines make it possible to run the batch file with no argument as well as with first argument being 2 or 64 or 83952 or an invalid string for a number consisting only of digits. The passed string is either extended or truncated to two digits on having less or more than two digits.

The external command FINDSTR is executed with full qualified file name to search literally and case-sensitive at beginning of all lines in file watched.txt in directory of the batch file for the string Suits.S03E%justPlayedNumber%.720p with %justPlayedNumber% replaced before by the Windows command processor cmd.exe by the two digit string.

FINDSTR exits with a value greater or equal 1 if the file watched.txt does not exist at all or the searched string was not found in the file. In this case the search string is appended to the file respectively the file is created with the search string on file not existing at all.

Then the lines in the file are sorted using the command SORT. That would not be really needed, but makes the lines easier to read by a user viewing this text file in a text editor or another application.

Read the Microsoft article about Using command redirection operators for an explanation of >> and >nul and 2>nul.

To understand the commands used and how they work, open a command prompt window, execute there the following commands, and read the displayed help pages for each command, entirely and carefully.

  • echo /?
  • endlocal /?
  • findstr /?
  • for /?
  • goto /?
  • if /?
  • setlocal /?
  • sort /?
Mofi
  • 46,139
  • 17
  • 80
  • 143
  • Thanks. My batch file is running perfectly now with the correct result each and every time. I actually started making batch files three days ago with no prior knowledge. Thanks again @Mofi – mzn Jan 12 '20 at 17:11
  • 1
    Nicely done. I never realized SORT /O allowed you to sort in place (input and output use same file) – dbenham Jan 13 '20 at 04:47
  • 1
    @dbenham Yes, input and output file can be the same. The description of parameter `/M` explains that indirectly. `sort` uses up to 90% of available RAM to first read entire input file to RAM and sort it there before writing the sorted data to output file. If there is not enough free RAM to load all input data into RAM, it uses a temporary file. It looks like the developer of `sort` thought that one main usage is to sort lines of a file and coded the `sort` executable accordingly. – Mofi Jan 13 '20 at 06:12
1

Heres an alternative to what you've posted, but with the intention of trying to maintain the methodology you'd attempted.

@Echo Off
Rem Disable delayed expansion to protect video names with exclamation marks.
SetLocal DisableDelayedExpansion

Rem Just played name from earlier in batch script.
Set "JPN=Suits.S03E08.720p"

Rem If the just played name is already inside watched.txt end script.
"%__AppDir__%find.exe" /I "%JPN%"<"watched.txt">NUL 2>&1 && GoTo :EOF 

Rem Generate episode number from the just played name variable content.
For %%I In ("%JPN%")Do Set "JPE=%%~nI"
Set "JPE=%JPE:~-2%"

Rem Delete any existing temporary text files.
Del "temp1.txt" "temp2.txt" 2>NUL
Rem For loop to read each line of content within watched.txt.
For /F "UseBackQTokens=*" %%I In ("watched.txt")Do (
    Rem Parse line to get episode number string. 
    Set "$WE=%%~nI"
    Rem Enable delayed expansion to access variable content set within do block.
    SetLocal EnableDelayedExpansion
    Rem Output lines to temporary files based upon earlier or later episode.
    If 1!$WE:~-2! Gtr 1%JPE% (>>"temp1.txt" Echo %%I)Else >>"temp2.txt" Echo %%I
    Rem End delayed expansion
    EndLocal
    Rem Undefine unnecessary variables.
    Set "$WE="
)
Rem Add just played name into sequence position within temporary file.
>>"temp1.txt" Echo %JPN%
Rem Overwrite watched.txt in sequence from the temporary files.
Copy /Y "temp1.txt"+"temp2.txt" /A "watched.txt" /B>NUL
Rem Clean up by deleting temporary files.
Del "temp1.txt" "temp2.txt" 2>NUL
Rem Prevent possible window closure to give time to view any messages.
Pause
Rem Undefine any remaining variables set within the script.
EndLocal
Rem Finish script execution.
GoTo :EOF

I have provided remarks for each line to help you to better understand the methodology. (Feel free to delete them from your production script, if you want.)

Lines 1, 3 and 6 should already exist in your unposted script code. Lines 12 and 13 is what I would suggest as a methodolody for automatically retrieving the episode number from the name string. (just in case you didn't think to do that yourself)

Compo
  • 36,585
  • 5
  • 27
  • 39