Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 554
DSharper Avatar asked Apr 28 '11 09:04

DSharper


1 Answers

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:
  • http://blogs.msdn.com/b/oldnewthing/archive/2006/08/23/714650.aspx

  • http://www.dostips.com/DtTutoFramework.php#_Toc128587174

  1. On substring replacement:
  • http://www.dostips.com/DtTipsStringOperations.php#Snippets.Replace
like image 99
Andriy M Avatar answered Sep 20 '22 15:09

Andriy M