0

I would like to pares XML files in folder and rename the folder based on tags in XML files

E:\Test\Folder1\meta.xml
E:\Test\Folder2\meta.xml
E:\Test\Folder3\meta.xml

Contents of meta.xml

<Title>XYZ</Title>
<Country>US</Country>
<Year>2014</Year>

Parse XML files in folder and rename them with values from three nodes

i have tried several methods , but i have problems with trailing slashes in the end

@ECHO OFF
SETLOCAL enableExtensions enableDelayedExpansion

SET "sourcedir=E:\Test\"

FOR /f "delims=" %%i IN ('findstr /s /m /L /c:"<Country>US</Country>" "%sourcedir%\*meta.xml"') DO (

REN "%%~dpi" "%%~dpi [US]"

)

am sure this is not the best method , i have tried several methods , but i have problems with trailing slashes

I expect the output of Soruce:

E:\Test\Folder1
E:\Test\Folder2
E:\Test\Folder3

to be:

E:\Test\Folder1 (2005) [US]
E:\Test\Folder2 (2004) [US]
E:\Test\Folder3 (2002) [US]

Rename Folders by Phrasing Years and countries from respective xml file in those folders

sai simha
  • 3
  • 2

3 Answers3

1

I suggest the following batch file for this task:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceDir=E:\Test"
for /F "eol=| delims=" %%I in ('dir "%SourceDir%\meta.xml" /A-D /B /S 2^>nul') do (
    set "Country="
    set "Year="
    for /F "tokens=1,2 delims=< > " %%A in ('%SystemRoot%\System32\findstr.exe /R "<Country>[^<][^<]*</Country> <Year>[^<][^<]*</Year>" "%%I"') do (
        if "%%A" == "Country" if not defined Country set "Country=%%B"
        if "%%A" == "Year" if not defined Year set "Year=%%B"
    )
    if defined Country if defined Year (
        set "FilePath=%%~dpI"
        setlocal EnableDelayedExpansion
        echo !FilePath:~0,-1!| %SystemRoot%\System32\findstr.exe /E /I /L /C:" (!Year!) [!Country!]" >nul
        for %%J in ("!FilePath:~0,-1!") do if not exist "%%~J (!Year!) [!Country!]" ren "%%~J" "%%~nxJ (!Year!) [!Country!]"
        endlocal
    )
)
endlocal

ATTENTION: The delimiters of second FOR are a left angle bracket <, a horizontal tab character, a right angle bracket > and a normal space character. So make sure to have those four characters in batch file after copying and pasting the code from the browser window.

The environment variable SourceDir is defined with a directory path without backslash at end because it is concatenated with \meta.xml in the next line. It would be syntactically not 100% correct to define this variable with E:\Test as this results in the file name string E:\Test\\meta.xml containing two backslash instead of just one between file path and file name.

For each file meta.xml found by command DIR in source directory or any of its subdirectories the command FOR executes the executable FINDSTR which searches case-sensitive (XML tags are case-sensitive) for country OR year elements with a string value of at least one character. The space character in search regular expression is interpreted by FINDSTR as OR which is not really good documented.

If environment variables Country and Year are defined both after searching for country and year strings in meta.xml, the file path of the current XML file is assigned to environment variable FilePath which always ends with a backslash.

Now it is time to enable delayed expansion. It should not be enabled before in case of a folder name contains one or more exclamation marks as in this case with delayed expansion already enabled at beginning of the batch file the batch file would not work at all. Please read this answer for details about the commands SETLOCAL and ENDLOCAL as much more happens than just enabling delayed environment variable expansion.

The entire folder name with path is output with ECHO and redirected to handle STDIN of FINDSTR to check if the folder name does not already end with (correct) year in round brackets and (correct) country in square brackets. This additional test is inspired by code written by LotPings.

The third FOR executed only if folder name does not already end with (correct) year and country is used to get from file path without the backslash at end the last folder name referenced with %%~nxJ which references the string after last backslash independent if this string represents a file name with file extension or a folder name. %%~J references the current folder name with full path without backslash at end.

Note 1: The batch file is not designed for being executed with source directory being root directory of a drive.

Note 2: The code does not remove a wrong year/country from folder name if the file meta.xml contains now a different year or country. So a folder named already Folder1 (2015) [US] is not renamed to Folder1 (2019) [India] if meta.xml in folder Folder1 (2015) [US] contains now <Country>India</Country> and <Year>2019</Year>.

Note 3: The line parsing done by FOR does not work if the XML file contains multiple XML elements on one line. Country and Year elements must be on separate lines with no other XML elements. So this batch code cannot be used if meta.xml contains for example <Country>India</Country><Year>2019</Year> in one line. Windows command processor has no features to parse XML content in XML manner like PowerShell supports.

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 /?
  • endlocal /?
  • findstr /?
  • for /?
  • if /?
  • ren /?
  • set /?
  • setlocal /?
Mofi
  • 46,139
  • 17
  • 80
  • 143
1
  • I'd use a for /d to iterate subfolders and check if a file meta.xml exists
  • when parsing the xml use the tags as variable names to store the values.

:: Q:\Test\2019\05\05\SO_55992361.cmd
@ECHO Off
SET "sourcedir=E:\Test\"
PushD "%Sourcedir%" ||(Echo can't locate %sourcedir% & pause & Exit /B 1)
For /D %%F in (Folder*) Do if exist "%%~fF\meta.xml" call :sub "%%~fF\meta.xml" "%%~fF"
PopD
Goto :Eof

:Sub
Set "Country="
Set "Year="
for /f "tokens=1-2delims=<>" %%A in ('findstr /i "country year" %1') Do set "%%A=%%B"
Echo %~2|findstr /EIC:" (%Year%) [%country%]">Nul 2>&1 && Goto :Eof
if defined Country if defined Year (
Echo Ren "%~2" "%~n2 (%Year%) [%country%]"
     Ren "%~2" "%~n2 (%Year%) [%country%]"
)

Sample output:

> Q:\Test\2019\05\05\SO_55992361.cmd
Ren "E:\Test\Folder1" "Folder1 (2005) [US]"
Ren "E:\Test\Folder2" "Folder2 (2004) [US]"
Ren "E:\Test\Folder3" "Folder3 (2002) [US]"

EDIT: changed batch due to @Mofis hints. And tested successfully on FAT32

  • 1
    There is one problem with this approach: When the folder names are really renamed and drive `E:` is not using NTFS, but FAT32 or exFAT, the result is wrong because of list of folders in file system changes during iteration by __FOR__. This is the reason why I used in my code __DIR__ to iterate with __FOR__ on a list of folder names not changing during loop execution. My drive for temporary files on which I run batch files for testing is a FAT32 drive which is the reason why I see issues caused by changing file allocation table entries of a directory during loop iterations. – Mofi May 06 '19 at 05:58
  • @Mofi Good points, spent another GB of Ram for another Ramdisk FAT32-formatted. Removed a flaw and inserted a test to not append again on successive runs. –  May 06 '19 at 08:29
  • Yes, the code works now also on FAT drives. It processes each folder containing `meta.xml` twice on a FAT32 or exFAT drive, but command line with __FINDSTR__ checking if folder name contains already country and year makes a good job to avoid renaming the folders again and again with appending year and country until folder name becomes too long. – Mofi May 06 '19 at 17:06
0
@ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "searchregex=<Country>.*</Country>"

FOR /f "delims=" %%i IN ('findstr /s /m /R "%searchregex%" "%sourcedir%\*meta.xml"') DO (
 rem here, %%i contains the name of the xml file.
 rem detect whether the subdirectory name ends with (year) [country]
 ECHO %%~dpi|FINDSTR /R ".*(.*) \[.*\]\\" >nul
 IF ERRORLEVEL 1 (rem no (year) [country] pattern found
  rem search that particular xml file for the Country tag and select the country to %%c
  FOR /f "tokens=2delims=<>" %%c IN ('findstr /R "%searchregex%" "%%i"') DO (
   rem repeat the search of the xml file for the Year tag and select the year to %%y
   FOR /f "tokens=2delims=<>" %%y IN ('findstr /R "%searchregex:Country=Year%" "%%i"') DO (
    rem now rename - note that a rename target may only be the NAME(extension)
    FOR /f "delims=" %%b IN ("%%~dpi.") DO ECHO REN "%%~fb" "%%~nxb (%%y) [%%c]"
   )
  )
 )
)

GOTO :EOF

Well - that was interesting. I've added in a few things to comply with my understanding of your goal.

First, I set up a regular expression searchregex because that string was going to be used a number of times and I couldn't face fixing it in multiple places.

First, search for your meta.xml files. If the meta.xml file is contained in a directory which ends (year) [country] then I suppose you don't want to rename that directory otherwise you'd get E:\Test\Folder1 (2005) [US] turned into E:\Test\Folder1 (2005) [US] (2005) [US] SO, if the drive:path matches the regex {anystring}({anystring}) [{anystring}]\ then you don't want to process it and errorlevel is set to 0. Note: For safety, perhaps add the /E switch to the findstr so that pattern only gets matched on the outer leaf.)

With those that require renaming, search the particular meta.xml file (I presume you want any filenames ending meta.xml) for the country tag using searchregex and then search the same file again, this time replacing Country in the search by Year which I accomplished using string-substitution because that seemed appropriate at the time.

If we reach the inner command, then we can drop the terminal \ from the pathstring by appending . and then resolving it using a for/f on the constant-string with delims= to get over tokenising.

And finally construct the REN command and ECHO it so that the routine can be tested.

Removing the echo from the echo ren will turn on the rename function.


@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION 
SET "sourcedir=U:\sourcedir"
SET "searchregex=<Country>.*</Country>"

FOR /f "delims=" %%i IN ('findstr /s /m /R "%searchregex%" "%sourcedir%\*meta.xml"') DO (
 rem here, %%i contains the name of the xml file.
 rem detect whether the subdirectory name ends with (year) [country]
 ECHO %%~dpi|FINDSTR /R ".*(.*) \[.*\]\\" >nul
 IF ERRORLEVEL 1 (rem no (year) [country] pattern found
  rem search that particular xml file for the Country tag and select the country to %%c
  FOR /f "delims=" %%c IN ('findstr /R "%searchregex%" "%%i"') DO (
   SET "countryline=%%c"
   SET "countryline=!countryline:*<=<!"
   FOR /f "tokens=2delims=<>" %%s IN ("!countryline!") DO (
    rem repeat the search of the xml file for the Year tag and select the year to %%y
    FOR /f "tokens=2delims=<> " %%y IN ('findstr /R "%searchregex:Country=Year%" "%%i"') DO (
     rem now rename - note that a rename target may only be the NAME(extension)
     FOR /f "delims=" %%b IN ("%%~dpi.") DO ECHO REN "%%~fb" "%%~nxb (%%y) [%%s]"
    )
   )
  )
 )
)

GOTO :EOF

This version sets countryline to the contents of the country line, then replaces any characters appearing before and including the first < with < using delayed expansion, then tokenising is applied to the resultant string using <> only to preserve the space in "Burkina Faso".

Magoo
  • 77,302
  • 8
  • 62
  • 84
  • Thank you very much , i have a problem seems variable from xml are not working . REN "E:\Renametest\Folder1" "Folder1 (Year) [Country]" REN "E:\Renametest\Folder2" "Folder2 (Year) [Country]" – sai simha May 05 '19 at 17:58
  • Sadly "Not working" means "Isn't doing what I expect it to do". You do realise that `DO ECHO REN` needs to be replaced by `DO REN` after you've finished testing in order to activate the `rename` don't you? Are the correct year and country being reported? – Magoo May 05 '19 at 19:49
  • Yes, I do realise that DO ECHO REN needs to be replaced by DO REN, the problem is , year and country are not being reported. – sai simha May 05 '19 at 20:16
  • So the screen-report report actually contains the literal `Year` and `Country`? I'd suggest that you try replacing `delims=<>" %%` in both places it occurs with delims=<> " %%` - that is place a space after `<>`. I suspect that your `*meta.xml` files may contain leading spaces on the target lines A downside to this solution would be that if the Country tag contains a space like "Burkina Faso", then. "country" will become only the first word of the country name. – Magoo May 05 '19 at 20:26