3

I want to run a .bat script which makes a file and save it with the same name as the folder it is in. I want it to start in a map and loop through the submaps. I started with this code below and run it in one of the submaps. That works, I get a file with the name of the submap.

FOR /D %%I IN ("%CD%") DO SET _LAST_SEGMENT_=%%~nxI
ECHO Last segment = "%_LAST_SEGMENT_%"
echo.>%_LAST_SEGMENT_%.mp4
pause

So now I only have to place it in a for loop. So I made the code below, which is not working. It seems that the %CD% is referring to my parent map(subfolders), where the .bat file is located, instead of my submaps (video1, video2, etc.).

Set Base=C:\Video\subfolders
FOR /R "%Base%" %%F IN (.) DO (
    PushD "%%F"
        FOR /D %%I IN ("%CD%") DO SET _LAST_SEGMENT_=%%~nxI
        echo %CD%
        ECHO Last segment = "%_LAST_SEGMENT_%"
        echo.>%_LAST_SEGMENT_%.mp4
    PopD
)
Pause

Output:

C:\Video\subfolders>Set Base=C:\Video\subfolders
C:\Video\subfolders>FOR /R "C:\Video\subfolders" %F IN (.) DO (
PushD "%F"
 FOR / %I IN ("C:\Video\subfolders") DO SET _LAST_SEGMENT_=%~nxI
 echo C:\Video\subfolders
 ECHO Last segment = ""
 echo.   1>.mp4
 PopD
)
C:\Video\subfolders>(
PushD "C:\Video\subfolders\."
 FOR / %I IN ("C:\Video\subfolders") DO SET _LAST_SEGMENT_=%~nxI
 echo C:\Video\subfolders
 ECHO Last segment = ""
 echo.   1>.mp4
 PopD
)
C:\Video\subfolders>SET _LAST_SEGMENT_=subfolders
C:\Video\subfolders
Last segment = ""
C:\Video\subfolders>(
PushD "C:\Video\subfolders\video1\."
 FOR / %I IN ("C:\Video\subfolders") DO SET _LAST_SEGMENT_=%~nxI
 echo C:\Video\subfolders
 ECHO Last segment = ""
 echo.   1>.mp4
 PopD
)
C:\Video\subfolders\video1>SET _LAST_SEGMENT_=subfolders
C:\Video\subfolders
Last segment = ""
C:\Video\subfolders>(
PushD "C:\Video\subfolders\video2\."
 FOR / %I IN ("C:\Video\subfolders") DO SET _LAST_SEGMENT_=%~nxI
 echo C:\Video\subfolders
 ECHO Last segment = ""
 echo.   1>.mp4
 PopD
)
C:\Video\subfolders\video2>SET _LAST_SEGMENT_=subfolders
C:\Video\subfolders
Last segment = ""
C:\Video\subfolders>(
PushD "C:\Video\subfolders\video3\."
 FOR / %I IN ("C:\Video\subfolders") DO SET _LAST_SEGMENT_=%~nxI
 echo C:\Video\subfolders
 ECHO Last segment = ""
 echo.   1>.mp4
 PopD
)
C:\Video\subfolders\video3>SET _LAST_SEGMENT_=subfolders
C:\Video\subfolders
Last segment = ""
C:\Video\subfolders>Pause
Press any key to continue . . .

I saw the link below which explain to use setlocal enabledelayedexpansion but I can’t get this to work.

https://superuser.com/questions/503488/why-doesnt-cd-work-after-using-pushd-to-a-unc-path

Any advice would be appreciated

Zanzi
  • 35
  • 3

2 Answers2

1

It looks like you want something like:

@echo off
for /F "delims=" %%I in ('dir "C:\Video\subfolders\*" /AD-H /B /S 2^>nul') do (
    pushd "%%I"
    echo/>"%%~nxI.mp4"
    popd
)

The commands pushd and popd are not really necessary as demonstrated with below code:

@echo off
for /F "delims=" %%I in ('dir "C:\Video\subfolders\*" /AD-H /B /S 2^>nul') do (
    echo Directory is: %%I
    echo/>"%%I\%%~nxI.mp4"
)

The command FOR runs in a separate command process started with cmd.exe /C in background the command line:

dir "C:\Video\subfolders\*" /AD-H /B /S 2>nul

The command DIR outputs to handle STDOUT of this background command process

  • in bare format because of option /B just all names with full path because of option /S
  • of non-hidden directories because of option /AD-H (attribute directory and not hidden)
  • in specified directory C:\Video\subfolders and all its subdirectories.

The error message output by DIR to handle STDERR on not finding any directory entry in entire directory tree of C:\Video\subfolders is suppressed by redirecting it to device NUL.

Read the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line in a separate command process started in background.

FOR captures the output to STDOUT of started command process and processes this output line by line with ignoring empty lines and by default also lines starting with a semicolon which both do not occur here.

FOR would also split up every line into substrings on spaces/tabs and would assign just first space/tab separated string to specified loop variable I. Directory names can have spaces and for that reason delims= is used to define an empty list of string delimiters resulting in getting assigned to loop variable I always the entire full qualified directory name currently processed from captured DIR output.

The name of the directory without path is referenced with %%~nxI which is the string after last backslash. A directory name can contain also a dot like folder.name or start with a dot like .FolderName although both are unusual on Windows. For that reason it is necessary to use %%~nxI (name and extension) and not just %%~nI because of FOR does not verify if string referenced with %%I is a file or directory name.

%%~nI references the string between last \ (or / for compatibility with Linux paths replaced all first by \) if there is any backslash (or slash) at all or beginning of string if there is no \ (or /) and last dot if there is a . at all after already determined beginning of name string or end of string if there is no dot. %%~xI references the string beginning with last dot found after last directory separator. So the string assigned to loop variable I must not be a file/folder name of a really existing file/folder although this is the case here. %%~nxI would be an empty string if I holds a string ending with \ (or /).

Do not assign a string which can be referenced using a loop variable without or with a modifier to an environment variable as this would require the usage of delayed expansion as explained by help of command SET output on running in a command prompt window set /?.

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

  • dir /?
  • echo /?
  • for /?
  • popd /?
  • pushd /?

See also DosTips forum topic ECHO. FAILS to give text or blank line - Instead use ECHO/.

The second batch file code in question does not work because of not using delayed expansion or avoiding the usage of environment variables defined/modified inside a command block and referenced within same command block using syntax %variable%. The Windows command processor cmd.exe parses the entire command block from ( of first FOR to matching ) before executing the outer FOR the first time. See How does the Windows Command Interpreter (CMD.EXE) parse scripts? All %variable% references within this command block are replaced during this preprocessing phase already by current value of the referenced environment variable. So the code finally executed on iterating outer FOR does not contain anymore any %variable% environment variable reference as it can be seen on command lines output by cmd.exe before executing the command line.

Mofi
  • 38,783
  • 14
  • 62
  • 115
  • Thank you for your answer and extensive explanation! The second piece of code worked exactly as i needed. And by the explanation I now know what it does as well. – Zanzi Dec 07 '18 at 07:24
0

While Mofi's answer resolved OP's issue, it doesn't actually answer the question originally asked.

The reason %CD% doesn't work inside a FOR loop is that you need to enable "delayed expansion"

For example:

SETLOCAL EnableDelayedExpansion
FOR %%i in (foo bar) DO (
    pushd %%i
    echo CD with delayed expansion: !CD!
    echo CD without it: %CD%
)

Assuming the "foo" and "bar" directories exist when you run that, you should see that !CD! gives the correct path.

More examples: Example of delayed expansion in batch file

jwd
  • 9,860
  • 3
  • 35
  • 59