I need to match or replace an asterisk * in a batc

2019-02-10 17:42发布

问题:

I'm trying to remove an asterisk from an environmental variable string, but can't seem to do it.

I'm creating an m3u file based around search strings, so for instance I if I want to make an m3u file containing every song with the word love in it, I would enter:

m3u *Love*

And m3u.bat would create the file:

xLovex.m3u

But the regular method of replacing characters does not work with an asterisk. (Though I don't have that problem with the question mark.)

set nam=%nam:*=x%.m3u

Instead creates the filename

x.m3u

回答1:

The easy answer is no.

The problem that you're encountering stems from the fact that the asterisk * is a special character when used with the SET search and replace method. It matches multiple characters in a limited, but still useful, way. You can learn about that here.

The hard answer is Yes!

I will provide you with two solutions. One an incomplete solution but elegent, the other complete and inelegent.

Both methods will search for * and replace it with an x.
Both methods will both search and modify the following string:

*love*

The first method that comes to mind is using a 'FOR /L' statement, and requires that you know how many characters long the environmental variable is.

::Major Edit::

I thought I knew the various maximum size strings of environmental variables, but dbenham has taken me to school, shown me a kick-in-the-behind length function, and in the mean time completely reversed my opinions of the two solutions I'm presenting.

Other than for the Windows 95/98/ME limitation of a 256 Character maximum environmental variable size. It seems that all versions of Windows using CMD.EXE have a limitation of 8,192 characters, well below what the documentation suggests.

Both versions require delayed environmental variable expansion, but for two different reasons. One because I'm operating inside a FOR statement. The other because you cannot put a % pair inside another % pair because the command processor matches the second % that it encounters to the first one it encounters, but we need to use a variable inside another variable expression. (You'll see.)

This solution uses the strLen function (in line 3) from DosTips.com that can be found Here. Just slap it into a file called strLen.bat and be amazed at it's speed!

Solution 1: (FOR /L Solution) :: Preferred Solution ::

setlocal ENABLEDELAYEDEXPANSION
set nam=*love*
call strLen nam len
for /l %%x in (0,1,%len%) do if not "!nam:~%%x,1!"=="" if "!nam:~%%x,1!"=="*" (
    set /a plusone=%%x+1
    for /l %%y in (!plusone!, 1, !plusone!) do (
        set nam=!nam:~0,%%x!x!nam:~%%y!
    )
)
echo %nam%
ENDLOCAL

I think this is a quick and elegant solution It could be sped up by adding the contents of strLen.bat to the routine, but I wanted no confusion as to the author.

If you, for some reason, do not wish to use strLen, then the next quickest method would probably use a GOTO loop.

Solution 2: (Goto Solution)

setlocal ENABLEDELAYEDEXPANSION
set nam=*love*
set num=0

:loop
    set /a plusone=%num%+1
    if "!nam:~%num%,1!"=="*" set nam=!nam:~0,%num%!x!nam:~%plusone%!
    set /a num=%num%+1
if not "!nam:~%num%,1!"=="" goto :loop

echo %nam%
EndLocal

Special thanks to dbenham for pointing out the strLen function. It works faster than any batch based function has a right to!



回答2:

Another solution to the stated problem is to use a PowerShell replace command within your batch script.

set var=*Love*
echo %var%>var.txt | powershell -command "((get-content var.txt) -replace '[\x2A]','x') -replace '.{1}$' | set-content var.txt"
set /p var=<var.txt
set var=%var%.m3u
echo %var%

In the above code, the second line

  • writes your string into a text file
  • calls a PowerShell command to get the contents of that file
  • replaces the * character with null
  • overwrites the text file with the new value

Once that is done, you read the value back into your variable.

To further explain the replace command, the first single quotes is what you are searching for. We are using square brackets to identify the * character as a hex character (\x2A is the hex value for *). After the comma, the second set of single quotes contains no value so that the searched object is removed. To prevent a space between xLovex and the .m3u, we have to use -replace '.{1}$' before writing the result to the text file.

Once you are done with the text file, enter a line to delete it.

if exist var.txt del var.txt


回答3:

Although there were already some very good and robust ways explained here, I'd still like to add another option for the sake of completion.

It's not as good as the other options but I personally use it in some cases where I'd like to keep the code clean and where I know that it will suffice:

The way it works is by using for /f's delims to cut the string into two parts, which are then put back together, getting rid of the * in the process:

for /f "tokens=1,* delims=*" %%a in ("a*b") do (set string=%%a%%b)

>>> string=ab

Obviously, the downside to this is that it can only be used to remove one *.

To remove more, we can either just use more tokens...

for /f "tokens=1-3,* delims=*" %%a in ("a*b*c*d") do (set string=%%a%%b%%c%%d)

>>> string=abcd

... or we can put the first line in a for /l-loop:

setlocal enableDelayedExpansion
set string=a*b*c*d
for /l %%a in (1, 1, 3) do (
    for /f "tokens=1,* delims=*" %%b in ("!string!") do (set string=%%b%%c)
)

>>> string=abcd

Another thing to note is that you can define more than one character in delims, and they will all be removed at once:

for /f "tokens=1,* delims=+-*/" %%a in ("a*-/+b") do (set string=%%a%%b)

>>> string=ab


回答4:

See this answer, and with set-ast.bat you'll want to put set-ast nam "x" in your file where needed.

set-ast takes the parameters <variable-to-modify> <string-to-replace-asterisks-with>