How to find and replace a string with a wildcard in a batch file?

I'm new to all of this so take it easy on me. I'm trying to automate a process where I change the port for a specific service. The port is determined in the file streamstack.exe.config. I have something that will find and replace a specific string within the file however in some situations I won't know the defined port number. Is it possible to find a string that contains a wildcard and replace with a predefined string? For example find "localhost:.*" and replace with "localhost:80"?

I have been able to use findstr with a wildcard, however I haven't been able to use some sort of replace with the output. I've also found something that will find a replace using setlocal enabledelayedexpansion, however I cannot figure out how to use wildcard with it.

My code is:

@echo off

set "replace=localhost:.*"
set "replaced=localhost:80"

set "source=c:\blah\blah\streamstack.exe.config"
set "target=c:\blah\blah\streamstack1.exe.config"


setlocal enableDelayedExpansion
(
   for /f "tokens=1* delims=:" %%a in ('findstr /N "^" %source%') do (
      set "line=%%b"
      if defined line set "line=!line:%replace%=%replaced%!"
      echo(!line!
   )
) > %target%
endlocal

I was hoping it would locate the string that includes localhost: followed by any group of numbers representing the port, and replace with localhost:80, which is the port I need the service to be using.

3 answers

  • answered 2019-06-24 21:54 David Herzfeld

    I decided to go a different route and call on powershell instead of trying to get all of this done in a bat file.

    (Get-Content C:\Users\David\Desktop\streamstack.exe.config) | ForEach-Object { $_ - 
    replace "http://localhost:.*/", "http://localhost:80/" } | Set-Content 
    c:\blah\blah\streamstack.exe.config
    

    This seems to do the job, and I am able to execute it from within a bat using...

    powershell -executionPolicy bypass -noexit -file 
    

  • answered 2019-06-25 03:27 dbenham

    You haven't given any details about the format of the config file.

    Assuming there is one name/value setting per line, and the order of the lines does not matter, then there is a very simple batch solution that is also very efficient. Use FINDSTR to write all lines except the localhost line, and then append your desired localhost line.

    >"%target%" (findstr /v "^http://localhost:" "%source%" & echo http://localhost:80)
    

    Of course the above will not work if the order of the lines is important.

    You ought to check out my JREPL.BAT regular expression find/replace utility. It is very powerful, yet simple to use. It is pure script (hybrid batch/JScript) that runs on any Windows machine from XP onward.

    Using the same find/replace you used in PowerShell, the JREPL solution would be

    call jrepl "(http://localhost):.*" "$1:80" /f "%source%" /o "%target%"
    

    I used a regular expression capture group to save a bit of typing.

    JREPL has loads of options, and full documentation is built-in to the utility. Use jrepl /?help to get a description of all the types of help that are available.

  • answered 2019-06-25 11:47 aschipfl

    Since you are trying to replace the port number in a URL I extended the search string a bit in order to include the leading :// after the protocol definition http or https and before the host name, and the trailing / after the port number (this also complies with your own answer).

    The following script replaces the port number only in the first occurrence of the desired URL, which is found when a string like :// plus the given host name plus : plus a purely numeric port number plus / is encountered:

    @echo off
    setlocal EnableExtensions DisableDelayedExpansion
    
    rem // Define constants here:
    set "_SOURCE=c:\blah\blah\streamstack.exe.config" & rem // (original file)
    set "_TARGET=c:\blah\blah\streamstack.exe.config" & rem // (modified file)
    set "_TEMP=%TEMP%\%~n0_%RANDOM%.tmp"              & rem // (temporary file)
    set "_HOST=localhost"                             & rem // (host name)
    set "_PORT=80"                                    & rem // (new port name)
    
    rem // Define to write to console if no target file is given:
    if not defined _TARGET set "_TEMP=con"
    
    rem // Store line-feed in variable:
    (set ^"_LF=^
    %= blank line =%
    ^")
    
    rem // Write output to temporary file:
    > "%_TEMP%" (
        rem // Read source file line by line, precede each line by line number plus `:`:
        for /F "delims=" %%L in ('findstr /N "^" "%_SOURCE%"') do (
            rem // Store currently iterated line, reset buffer for part left to host name:
            set "LINE=%%L" & set "LEFT="
            rem // Toggle delayed expansion to avoid trouble with exclamation marks:
            setlocal EnableDelayedExpansion
            rem // Get line portion right to `://` + host name + `:`:
            set "RIGHT=!LINE:*://%_HOST%:=!"
            rem // Check whether host name part has really been found:
            if not "!LINE!" == "!RIGHT!" (
                rem /* Buffer right line portion in a `for` variable
                rem    to transport it over the `endlocal` barrier: */
                for /F "delims=" %%J in (^""!RIGHT!"^") do (
                    rem // Replace `://` + host name + `:` by a line-feed:
                    for /F "delims=" %%I in (^"!LINE:://%_HOST%:^=^%_LF%%_LF%!^") do (
                        rem /* Ensure to only process first line of multi-line string
                        rem    to get the line portion left to `://`+ host name + `:`: */
                        if not defined LEFT (
                            endlocal
                            rem /* Store line portion left to `://` + host name + `:`,
                            rem    restore right line portion from `for` variable: */
                            set "LEFT=%%I" & set "RIGHT=%%~J"
                            rem /* Split off everything behind the first `/`, which
                            rem    constitutes the port number: */
                            for /F "delims=/" %%F in ("0%%~J") do (
                                rem /* Check whether port number is purely numeric;
                                rem    this is done by checking whether a `for /F` loop
                                rem    iterates if numerals are defined as delimiters: */
                                for /F "delims=0123456789" %%E in ("%%F") do rem/
                            ) && (
                                rem // `for /F` loop iterated, hence keep current line:
                                setlocal EnableDelayedExpansion
                            ) || (
                                rem // `for /F` loop did not iterate, change port number:
                                setlocal EnableDelayedExpansion
                                set "LINE=!LEFT!://%_HOST%:%_PORT%/!RIGHT:*/=!"
                            )
                        )
                    )
                )
            )
            rem // Return line string:
            echo(!LINE:*:=!
            endlocal
        )
    )
    rem /* Move temporary file onto target file;
    rem    this allows source and target files to be identical: */
    if defined _TARGET > nul move /Y "%_TEMP%" "%_TARGET%"
    
    endlocal
    exit /B