Get variable from string with random numbers of delimiter [cmd]

Viewed 117

I have a folder with many delimiters "_": text4_text5_text6_text7_2000-01-02

I need to create from it 2 vars. 1st one is everything from foldername until last delimieter. 2nd one is after last delimiter:

  • %name% = text4_text5_text6_text7
  • %date% = 2000-01-02

For date I have this code:

SET DIR = text4_text5_text6_text7_2000-01-02
FOR %%a in (%DIR:_= %) do set DATE=%%a

But I can't figure out hot to take into %NAME% everything else before %DATE%.

I will appreciate any tip.

6 Answers

If the format is as you mentioned, with the last _ always containing the date after:

@echo off
setlocal enabledelayedexpansion
set "name=text4_text5_text6_text7_2000-01-02"
for %%i in (%name:_= %) do set "mydate=%%i"
set "restofline=!name:%mydate%=!"
echo %restofline:~0,-1% %mydate%

Here %mydate% is the variable containing the date and %restofline% contains the rest.

I think this is the simplest way to do this:

@echo off
setlocal EnableDelayedExpansion

set "dir=text4_text5_text6_text7_2000-01-02"

set "name="
set "last=%dir:_=" & set "name=!name!_!last!" & set "last=%"
set "name=!name:~1!"

echo name="%name%"
echo date="%last%"

This method works via a simple substring replacement that produces several commands. You may appreciate it by removing the @echo off line and reviewing the executed code in the screen. For a further explanation on this method, see this thread.


If you want a more standard method, then this one is simple:

@echo off
setlocal EnableDelayedExpansion

set "dir=text4_text5_text6_text7_2000-01-02"

for %%a in ("%dir:_=.%") do set "last=%%~Xa"
set "last=%last:~1%"
set "name=!dir:_%last%=!"

echo name="%name%"
echo date="%last%"

You can put all values into array of string. Last value of array must be %date%.

@ECHO OFF
SET name=text4_text5_text6_text7_2000-01-02
SET /A id=0
SETLOCAL ENABLEDELAYEDEXPANSION
FOR %%a in (%name:_= %) DO (
SET /A id+=1
SET arr[!id!]=%%a
)

REM PRINT ALL VALUES WHERE %id% IS THE LAST ARRAY NUMBER
FOR /L %%i in (1,1,%id%) DO @ECHO !arr[%%i]!

REM PRINT ONLY DATE
@ECHO !arr[%id%]!

ENDLOCAL

Here is a method that applies the sometimes so-called code injection technique:

@echo off
setlocal EnableExtensions EnableDelayedExpansion

rem // Define (non-empty) input string here:
set "name=text4_text5_text6_text7_2000-01-02"

rem // Clear variables:
set "rest=" & set "last="
rem // Split off the last `_`-separated item using the code injection technique:
set "rest=%name:_=" & (if defined last set "rest=!rest!_!last!") & set "last=%"

rem // Return result:
set name
set rest
set last

endlocal
pause

The related console output is going to be the following:

name=text4_text5_text6_text7_2000-01-02
rest=text4_text5_text6_text7
last=2000-01-02

To find out how it works change @echo off to @echo on and check the console output. This method is based on sub-string substitution, and it requires delayed variable expansion.

Regard, that this approach will fail with a string that contains !, ^ and ".

Notice, that multiple consecutive separators become collapsed into one, so a string like abc__def__01 would be split into abc_def and 01. To avoid that and thus retain the separators, change the core code to this:

rem // Clear variables:
set "rest=" & set "last=" & set "flag="
rem // Split off the last `_`-separated item using the code injection technique:
set "rest=%name:_=" & (if defined flag (set "rest=!rest!_!last!") else set "flag=#") & set "last=%"

For more information about this method refer to split string into substrings based on delimiter [DosTips].

Here is an approach based on your code using a standard for-loop:

@echo off
setlocal EnableExtensions EnableDelayedExpansion

rem // Define (non-empty) input string here:
set "name=text4_text5_text6_text7_2000-01-02"

rem // Reset variables:
set "rest=_" & set "last="
rem // Split off the last `_`-separated item using a `for` loop:
for %%a in ("%name:_= %") do (
    rem // This assignment is always one iteration behind:
    if defined last set "rest=!rest!_!last!"
    rem // This value is available for the next iteration:
    set "last=%%a"
)
rem // Strip off leading `__`:
set "rest=%rest:~2%"

rem // Return result:
set name
set rest
set last

endlocal
pause

Regard, that this approach will fail with a string that contains wildcards like ?, * and <, >; moreover, !, ^ and " must not occur in the string.

To at least avoid troubles with !, ^ and " in the string, use this more robust but more complex code instead:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define input string here:
set "name=text4_text5_text6_text7_2000-01-02"

rem // Reset variables:
set "rest=_" & set "last=" & set "flag="
rem // Double quotes to avoid issues with such:
setlocal EnableDelayedExpansion
set "name=!name:"=""!^" & rem ^"
rem // Split off the last `_`-separated item using a `for` loop:
if defined name for %%a in ("!name:_=" "!") do (
    rem // Use `for /F` to transfer string beyond `endlocal` barrier:
    for /F "delims=" %%b in ("!rest!_!last!") do (
        rem // This assignment is always one iteration behind:
        endlocal & if defined flag set "rest=%%b"
    )
    rem // This value is available for the next iteration:
    set "last=%%~a" & set "flag=#"
    rem // Toggle delayed expansion to avoid issues with `!` and `^`:
    setlocal EnableDelayedExpansion
)
rem // Revert doubling of quotes:
set "rest=!rest:""="!^" & rem ^"
if defined last set "last=!last:""="!^" & rem ^"
rem // Strip off leading `__`:
set "rest=!rest:~2!"

rem // Return result:
set name
set rest
set last
endlocal

endlocal
pause

One way to do it in a cmd batch-file on windows would be to use a regex in PowerShell. If you are on a supported Windows system, PowerShell is installed.

Note that I changed the result variable name DATE to THE_DATE. DATE is an automatic variable in cmd and should not be used separately.

C:>type getlastpart.bat
@ECHO OFF
SET "NAME=text4_text5_text6_text7_2000-01-02"
FOR /F "delims=" %%A IN ('powershell -NoLogo -NoProfile -Command ^
    "'%NAME%' -match '(.*)_(.*)' | Out-Null; $matches[2]"') DO (SET "THE_DATE=%%~A")
ECHO THE_DATE is set to "%THE_DATE%"

C:>CALL getlastpart.bat
THE_DATE is set to "2000-01-02"

C:>echo %THE_DATE%
2000-01-02
Related