How to replace string inside a bat file with command line parameter string

84,171

Solution 1

First of all, you'll have to store %1 into a variable, then you will be able to perform the replacements.

Basically, the syntax for the replacement is this:

%variable:str1=str2%

which means: 'replace every str1 in variable with str2'.

In your case both str1 and str2 are parameters, not literal strings. Using the above template directly you might end up with this expression:

%variable:%3=%4%.

But that would confuse the parser, as it wouldn't know that %3 and %4 should be evaluated first. In fact, it would first try to evaluate %variable:% (and fail).

One of the solutions in this case could be to use a method called 'lazy' delayed evaluation. Basically, you are passing the command where you are evaluating a variable, to the CALL command. The transformation of the original command to its 'CALL version' is like so:

ECHO %var% ==> CALL ECHO %%var%%.

Note the double %s. At parse time they are evaluated to single %s. The resulting command would be parsed again by CALL, and the ultimate effect would be the same as in case of the original command, ECHO %var%.

So it works the same as the original command (which is good), and what we are gaining here is the later time of evaluation, I mean the final evaluation, when the variable is actually replaced with its value. Knowing about that effect, we can construct our expression in such a way that %3 and %4 are evaluated first, and then the entire resulting expression. Specifically, like this:

%%variable:%3=%4%%

After the first parse this expression would become something like this:

%variable:x=y%

That would be parsed again, and the output would be variable's modified contents.

For better illustration, here's a simple working example:

SET "output=%1"
CALL SET output=%%output:%3=%4%%
ECHO %output%

UPDATE

There's another method of doing the same thing, which I should probably have mentioned first.

The Windows command shell supports a proper delayed expansion. It is simpler in use, but has some caveats.

First, how to use it. The syntax for delayed expansion is !var! instead of %var% for immediate expansion (which remains valid and can be used alongside with the delayed expansion syntax).

Probably !var! will not work in your script until you enable the syntax with the command:

SETLOCAL EnableDelayedExpansion

The ENDLOCAL command closes the block within which the delayed expansion syntax is valid and interpreted by the command shell.

The above example script could be rewritten like this:

SET "output=%1"
SETLOCAL EnableDelayedExpansion
SET output=!output:%3=%4!
ECHO !output!
ENDLOCAL

So how this works in case of the SET output=!output:%3=%4! command:

  • %3 and %4 are evaluated immediately, i.e. at the parse time – they are replaced with x and y respectively;

  • the command becomes this: SET output=!output:x=y!;

  • the command is about to execute – the ! expression is evaluated (xs are replaced with ys);

  • the command is executed – the output variable is modified.

Now about the caveats. The first thing to remember is that the ! becomes part of the syntax and is consumed and interpreted whenever encountered. So you'll need to escape it where you want to use it as a literal (like ^!).

Another caveat is the primary effect of a SETLOCAL/ENDLOCAL block. The thing is, all the changes to environment variables within such a block are, well, local. Upon exiting the block (upon executing ENDLOCAL) the variable is set to the value it had prior to entering it (prior to executing SETLOCAL). This means for you that the changed value of output will only be valid within the SETLOCAL block which you had to initiate for using the delayed expansion in the first place. Possibly this may not be a problem in your particular case, if you just need to modify the value and then use it right away, but you should probably have to remember it for the future.

Note: As per jeb's comment, you can save the modified value and leave the SETLOCAL block using this trick:

ENDLOCAL & SET "output=%output%"

The & operator simply delimits the commands when they are placed on the same line. They are executed one after the other, in the same order they are specified. The thing is, by the moment of parsing the line, the SETLOCAL block hasn't been left yet, so %output% evaluates to the modified value, which is still valid. But the assignment is actually executed after ENDLOCAL, i.e. after leaving the block. So you are effectively storing the modified value after leaving the block, thus preserving the changes. (Thanks, jeb!)


More information:

  1. On delayed expansion:
  1. On substring replacement:

Solution 2

I've tried below code in windows 7 batch file:

SET output=%1
CALL SET output=%output:unsigned=signed%
CALL SET output=%output:.apk=-aligned.apk%

It works !

Share:
84,171
DSharper
Author by

DSharper

Updated on March 16, 2021

Comments

  • DSharper
    DSharper about 3 years

    I have following in a cmd batch file:

    for /f %%l in (%2) do (for %%f in (%%l) do copy "%%f" %1))
    

    note : this script basically does is read a text file that contains semicolon delimited txt file whose path is given by %2(eg which contains c:\test1\file1.cs;d:\file2.js) and copy the files to destination folder specified by %1.

    I need to replace the %1 parameter's string value of x (which is also passed to batch file e.g. %3) with %4 value which is also passed as parameter to batch file.

    e.g.:

    if %1 = 'test replace x with y'
    %3=x
    %4=y
    

    so the output should be 'test replace y with y'

    How can I achieve this using Windows CMD batch interpreter?

  • jeb
    jeb about 13 years
    +1, But why you show the 'lazy' delayed evaluation, but not the (simpler) delayed expansion?
  • Andriy M
    Andriy M about 13 years
    @jeb: Thanks for the question! For some reason, don't know why, this didn't work for me when I was making tests (on Win7): set "output=%1"; setlocal EnableDelayedExpansion; set "output=!output:%3=%4!"; endlocal; echo %output%. Strangely, !output:%3=%4! worked in ECHO, but refused to have any effect with SET. The variable just wouldn't change.
  • jeb
    jeb about 13 years
    But that's ok, as you change the output var in the setlocal block, after the endlocal the output var has it's original value. Not a problem of Win7
  • Andriy M
    Andriy M about 13 years
    @jeb: Why, of course, how foolish of me! Thank you. But... the fact that the changed value expires after exiting the setlocal block means, you'll have to have the delayed expansion enabled for the entire block of commands where you need to use the modified value. Possibly not the problem with the OP's case. All right, I'll add that to my answer then. Thanks again!
  • jeb
    jeb about 13 years
    You can leave the block and hold the safe the modified value with ( endlocal & set "output=%output%")
  • jeb
    jeb over 9 years
    But that doesn't solve the question. The question was about replacing a string with unknown parameters in %3 and %4
  • dgo
    dgo over 5 years
    One caveat if you are using this to remove strings (like you might do with newvariable=!variable:str1=!), I could not get this to work without setting a variable for the empty string like set "str2=". Then if I did CALL SET newvariable=!!variable:%str1%=%str2%!!, it would remove str1 just as if I had passed an empty string instead of %str2% (which was an empty variable). Go figure.
  • Andriy M
    Andriy M over 5 years
    @dgo: That's curious. I've just tested your first method in Win 7, seemed to work as expected for me (the ECHO test--test line in the screenshot was ECHO %var2% in the script). Maybe I missed or misunderstood something.
  • dgo
    dgo over 5 years
    @AndriyM - You might be right. I'll look again when I have a minute - I was also inside a for loop (which may or may not make a difference). I'll look again a bit later, and then clean up my comments.
  • Andriy M
    Andriy M over 5 years
    @dgo: Ah yes, if it's a parenthesised block (for loop's body, if/else branch etc.) then "it depends". At least if I was both doing the removal and using the resulting value in the same block, I would be resolving var2 as !var2! instead of %var2%. I don't know if that's applicable to your specific scenario but it's just one of the more common gotchas I thought I'd mention just in case.