0

This is going to sound strange, but after switching to Windows 11, I found that the behavior of a batch .cmd file FOR loop has radically changed.

I'm working on MP4 file processing. During processing I do the following.

  1. Rename the the file from example.mp4 to example.mp4_original
  2. Use example.mp4_original to re-create example.mp4 with updated metadata
  3. do some additional stuff to example.mp4

I use this simple FOR loop to find and process all MP4 files in the directory:

for %%A in (.\*.mp4) do (    
    call :processing "%%~nxA"
)

Pre-Windows 11 this worked perfectly fine. The FOR loop would find each MP4 file in the directory, I would do the processing, and when I was done, my directory would look like this:

example1.mp4_original
example1.mp4
example2.mp4_original
example2.mp4
example3.mp4_original
example3.mp4

But with Windows 11, the FOR loop behavior has changed. It sounds weird, but his is what it is doing. If I just loop and print out filenames, I'll get a list of all MP4 files:

example1.mp4
example2.mp4
example3.mp4

However, when I do my processing and rename example1.mp4 to example1.mp4_original and then recreate example1.mp4, what the FOR loop in Windows 11 is now doing is somehow "finding" the new example1.mp4 and "adds" it to the FOR loop so effectively the FOR loop now loops forever reprocessing the files over and over again.

Now I thought there might be something wrong with my processing so I created a simple example that all it did was rename example1.mp4 to example1.mp4_original and then used the copy command to copy example1.mp4_original as example1.mp4. Sure enough, the FOR loop again somehow "finds" this new file and "adds" it to the FOR loop.

I have never seen behavior like this before. Anyone have any thoughts?

Michael Remijan
  • 687
  • 7
  • 22
  • 4
    It's always done this. You want to process the output of `dir /b *.mp4` with a `for /f` loop rather than just a regular `for` loop over `*.mp4` files. – SomethingDark Apr 06 '23 at 04:08
  • I disagree that it's always done this. I developed my code in 2019, using it without problems until switching to Windows 11 in 2023. – Michael Remijan Apr 06 '23 at 04:24
  • It's been doing this since _at least_ Windows 10, to the extent that I've answered questions on here about the problem you're experiencing, but I could see it maybe working in Windows 98 or earlier. – SomethingDark Apr 06 '23 at 04:45

2 Answers2

1

Based on SomethingDark's comment, I updated the FOR loop to:

for /f "tokens=*" %%f in ('dir /b .\*.mp4') do (
    echo "Do whatever you need to"
)

And this is working OK.

I also developed another solution to store the list of files into an array and process them by looping over the array

setlocal EnableDelayedExpansion
echo -- Build array of MP4 filenames
set "idx=-1"
for %%f in (.\*.mp4) do ( 
    echo "%%f"
    set /A "idx=!idx!+1"    
    set files[!idx!]="%%~nxf"
)
echo -- Loop over array
for /L %%x in (0,1,%idx%) do (
    echo !files[%%x]!
)

Both solutions work, but I'm sticking with solution based on SomethingDark's comment since it is more concise.

Michael Remijan
  • 687
  • 7
  • 22
  • The solution provided by SomethingDark being best `for /F "eol=| tokens=* delims=" %%I in ('dir *.mp4 /A-D /B 2^>nul') do ...` to work also for MP4 files of which file name begins with `;` or with one or more spaces (very rare) and exclude folders of which folder name ends with `.mp4` (also very rare) is also better in case of an MP4 file name contains one or more `!` with batch file containing at top `@echo off` and `setlocal EnableExtensions DisableDelayedExpansion`. That __FOR__ loop processes really all files with `.mp4` in short or long name at end in the current directory. – Mofi Apr 06 '23 at 04:56
  • See also: [At which point does FOR or FOR /R enumerate the directory (tree)?](https://stackoverflow.com/questions/31975093/) `cmd.exe` processes the __current__ file system entries by accessing the file system on each iteration of the loop since NT4 instead of getting first a list of matching file system entries loaded into the memory and process this loaded list. That behavior is always a problem on commands in the loop modify the list of matched file system entries by adding, modifying, renaming or deleting files/folders matched by the wildcard pattern used by __FOR__. – Mofi Apr 06 '23 at 05:02
  • 2
    A standard __FOR__ loop works on drives using NTFS on just modifying matched files as NTFS returns the list of file names sorted in a local specific alphabetic order. But other file systems like FAT32 or exFAT do not sort the file names alphabetically. The file/folder names list order is unpredictable on FAT16, FAT32, exFAT formatted storage medias and changes with each modification of a file which can result in skipping some files, processing some files more than once and even in an endless running loop on using a standard __FOR__ loop. – Mofi Apr 06 '23 at 05:10
0

If you want the idx numbering to start with the value '0', then do so. Then simply up the counter AFTER you assign the filename to the array.

setlocal EnableDelayedExpansion
echo -- Build array of MP4 filenames
set "idx=0"
for %%f in (.\*.mp4) do ( 
    echo "%%f"
    set files[!idx!]="%%~nxf"
    set /A "idx=!idx!+1"    
)

IMHO, it produces more transparent coding.

Klojum
  • 1
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Aug 07 '23 at 05:13