1

Question:

Is it possible to have a function receive a parameter which has both a percent sign and an exclamation mark?

Problem:

I am in a FOR /D processing directories in this fashion:

FOR /D %%d IN ("%~1\*") DO (
    CALL :process "%%~fd"
)

The problem comes when the subdirectory name (%%~fd) contains both a % and a ! (which is completely legal in Windows), such as C:\&!x#%. When I read %1 in the subprocedure :process, the percent sign disappears. If I EnableDelayedExpansion, then the exclamation mark does.

I read this post and, apparently, this cannot be solved. If delayed expansion is disabled, the % will be erased. If delayed expansion is enabled, the ! will be.

Example:

ex.bat:

@ECHO OFF
SET arg="%~1"
CALL :clean_echo %arg%
GOTO :EOF

:clean_echo
SET arg="%~1"
SET arg=%arg:&=^&%
SET arg=%arg:|=^|%
SET arg=%arg:<=^<%
SET arg=%arg:>=^>%
ECHO %arg:~1,-1%
GOTO :EOF

If I execute ex.bat "%!" the output is just !. The % is lost when it is passed on to the :clean_echo subprocedure.

Community
  • 1
  • 1

2 Answers2

3
@ECHO OFF
SETLOCAL
FOR /d %%a IN (*) DO (ECHO %%~fa
 SET "var=%%~fa"
 CALL :proc var
)

GOTO :EOF

:proc
FOR /f "tokens=1*delims==" %%x IN ('set %1 2^>nul') DO IF /i "%1"=="%%x" (
 ECHO %%y
)
GOTO :eof

You don't say enough about your application, but essentially passing such strings as a parameter is doomed to fail. Here's a method that may work for you, although it won't cope with the data starting with = (I've seen filenames containing =.)

It's a game of piggy-in-the-middle with the parser being piggy. So long as your data remains assigned to a metavariable it seems fine. Really depends on the unstated - precisely what you want to do with it.

Magoo
  • 77,302
  • 8
  • 62
  • 84
2

It could be done, you need to double the percents and prefix the exclamation marks with a caret.
But then it fails with single carets in the name and that can't be solved in a proper way.

As Magoo said, it's always better to use a variable and only transfer the variable name instead of the content.

But in the function you should use delayed expansion to expand the variable.

setlocal DisableDelayedExpansion

FOR /D %%d IN ("%~1\*") DO (
    set "var=%%~fd"
    setlocal EnableDelayedExpansion
    CALL :process var
    endlocal
)
exit /b

:process
set "content=!%1!"
echo !content!
exit /B
jeb
  • 78,592
  • 17
  • 171
  • 225
  • "It's always better to use a variable and only transfer the variable name instead of the content" Great piece of advice, thanks @jeb and @Magoo! –  Dec 27 '14 at 07:50