stringbatch-filefind

How to remove FIRST occurence of substring from a string?


I am working on and can only work with a batch file. No PowerShell.
I would like to remove the FIRST occurrence of a substring in a string.

@echo off 
@setlocal enableextensions enabledelayedexpansion

set "str=C:\help\te\Windows\Example\001 Example\te\005 Example"  

echo -----------Original string--------------------------- 
echo !str!  
 
echo ----------This removes EVERY \te\---------------------------- 
echo !str:\te\=\!  
 
echo -----------This gets everything after first \te\--------------------------- 
set "strAfter=!str:*\te\=\!"
echo !strAfter!  

echo ----------This gets everything before first \te\---------------------------- 
echo how?  

echo ---------simply get rid of FIRST \te\----------------------------- 
echo how?  

echo -----------------I could work with the position of the first \te\------------------------------- 
echo how?  

PAUSE

I've played with what is above. I've played with for loop, find, findstr.
I've also thought of getting the position of the first \te\ and then proceed with extracting the string before and the string after and finally concatenating. No success with anything.

UPDATE 1: With Magoo's trick, I'm able to make it work. Thank you. (Still reading up on double quotes, %%, && and !!.)

echo:
echo ------ This gets everything before first \te\--------
rem Replace "everything after \te\" with nothing, then remove \te again
set "strBefore=!str:%strAfter%=!"
set "strBefore=!strBefore:\te=!"
echo !strBefore!

echo:
echo ------ First method--------
set "finalstr=!strBefore!!strAfter!"
echo !finalStr!

I'm still curious to getting a position if ever I need it.

echo:
echo ----(Method2)-- I could work with the position of the first \te\-------- 
echo ------ if I could get the 7 automatically
set /a ind=7
set before=!str:~0,%ind%!
set after=!str:*\te\=\!
set finalstr=!before!!after!
echo !finalstr!

And curious, there is really no easy 1 or 2 line search for FIRST occurrence?

UPDATE 2: While the small testing script above worked, once I brought it back into the actual script, it breaks. Here's the official script.

@ECHO off
@setlocal enabledelayedexpansion 


REM find filenames that are links
For /R %%G in (*.lnk) do (
   echo: 
   echo --------------------------------
   set originalLink=%%G
   echo For this link  : !originalLink!
   
   REM find the Target/cible
   for /F "tokens=1 delims=" %%a in ('shortcutJS.bat -examine "%%G" ^| find /i "Target:"') do (
      set "targetVar=%%a"        
   )
   REM remove word Target: from front (use double quotes in case of spaces)
   set "targetVar=!targetVar:Target:=!"    
   echo Target file is : !targetVar! 

   rem TODO count how many \te\ to put an if to skip if useless

   REM remove or replace the FIRST folder named \te\ from path
   set "strAfter=!targetVar:*\te\=\!"
   echo strAfter first \te\: !strAfter!

   REM Replace "everything after \te\" with nothing, then remove \te again
   set "strBefore=!targetVar:%strAfter%=!"
   echo strBefore is: !strBefore!
   set "strBefore=!strBefore:\te=!"
   echo strBefore: !strBefore!
   set "targetNew=!strBefore!!strAfter!"
   echo targetNew !targetNew!

   REM this would remove every \te\... not good if more than one. trying to change with what's above.
   set targetNew=!targetVar:\te\=\!
   echo:
   echo New target     : !targetNew!

   REM actually push the new Target to the file...
   REM editShortcut.vbs "!originalLink!" "!targetNew!"
    
   PAUSE
)

strAfter works, but not strBefore. Here is one output:

--------------------------------
For this link  : C:\Users\carol\Desktop\tests raccourcis names serveur\renameLinks2\horloge velo.jpg.lnk
Target file is : C:\Users\carol\Desktop\tests raccourcis names serveur\te\horloge velo.jpg
strAfter first \te\: \horloge velo.jpg
strBefore is: targetVar:=
strBefore: targetVar:=
targetNew targetVar:=\horloge velo.jpg

New target     : C:\Users\carol\Desktop\tests raccourcis names serveur\horloge velo.jpg
Appuyez sur une touche pour continuer...

Solution

  • This one-liner works here:

    @echo off
    setlocal EnableDelayedExpansion
    
    set "str=C:\help\te\Windows\Example\001 Example\te\005 Example"
    echo Old: "%str%"
    
    set "repl=\" & set "new=%str:\te\=!repl!" & set "repl=\te\" & set "new=!new!%"
    echo New: "%new%"
    

    Output:

    Old: "C:\help\te\Windows\Example\001 Example\te\005 Example"
    New: "C:\help\Windows\Example\001 Example\te\005 Example"
    

    For a further description on the method used, see this answer

    You can also get the position of the first (or all) \te\ string using this similar method


    EDIT: New method added as response to comment

    The method above requires to use %standard% expansion in order to work. If you want to perform this replacement into a loop (that would require !delayed! expansion), you have two options:

    1- Use a subroutine to perform the replacement

    Just define a subroutine that get the string in the parameter, perform the replacement and returns the result in a variable, and call it from inside the loop:

    :replaceFirst
    set "str=%~1"
    set "repl=\" & set "new=%str:\te\=!repl!" & set "repl=\te\" &  set "new=!new!%"
    exit /B
    

    2- Replace the delimiter substring by a new-line character, and process the parts in a standard for /F command:

    set "new="
    for /F "delims=" %%a in (^"!str:\te\^=^
    %Don't remove this line%
    !^") do (
       if not defined new (
          set "new=%%a\"
       ) else (
          set "new=!new!%%a\te\"
       )
    )
    set "new=!new:~0,-4!"
    

    Below is the complete code that solve your problem:

    @echo off
    setlocal EnableDelayedExpansion
    
    REM find filenames that are links
    For /R %%G in (*.lnk) do (
       echo: 
       echo --------------------------------
       set originalLink=%%G
       echo For this link  : !originalLink!
       
       REM find the Target/cible
       for /F "tokens=1 delims=" %%a in ('shortcutJS.bat -examine "%%G" ^| find /i "Target:"') do (
          set "targetVar=%%a"        
       )
       REM remove word Target: from front (use double quotes in case of spaces^)
       set "targetVar=!targetVar:Target:=!"    
       echo Target file is : !targetVar! 
    
       rem TODO count how many \te\ to put an if to skip if useless
    
       REM remove or replace the FIRST folder named \te\ from path
       set "targetNew="
       for /F "delims=" %%a in (^"!targetVar:\te\^=^
    %Don't remove this line%
    !^") do (
          if not defined targetNew (
             set "targetNew=%%a\"
          ) else (
             set "targetNew=!targetNew!%%a\te\"
          )
       )
       set "targetNew=!targetNew:~0,-4!"
    
       echo:
       echo New target     : !targetNew!
    
       REM actually push the new Target to the file...
       REM editShortcut.vbs "!originalLink!" "!targetNew!"
        
       PAUSE
    )
    

    EDIT #2: Simpler method added

    The following code is a much simpler solution:

       REM remove or replace the FIRST folder named \te\ from path
       for %%a in ("!targetVar:*\te\=!") do set "targetNew=!targetVar:\te\%%~a=!\%%~a"
    
       echo:
       echo New target     : !targetNew!