I am pretty sure this must have been discussed and beaten to death before, but the advice I've found most often was to pass by reference and use the most generic return which handles everything from unbalanced quotes to embedded line breaks - and because of that is unnecessarily complex in the case of strings known to be better behaved.
In the simplest case of disableDelayedExpansion it's still needed
- for the caller to double the %s in the passed value (since 'call' would otherwise halve and/or try to expand them as variables), and
- for the callee to halve the ^s inside the received quoted strings (since 'call' would have doubled them).
A minimal example of this "dance" would be the following...
Code: Select all
@rem assume disableDelayedExpansion
:caller [in,ref] str
setlocal enableDelayedExpansion & set "v=!%1:%%=%%%%!"
endlocal & set "v=%v%"
call :callee "%v%" w
@rem at this point the value of variable %1 has been copied to variable w
goto :eof
:callee [in,val] src [out,ref] dst
setlocal disableDelayedExpansion
set "s=%~1"
set "s=%s:^^=^%"
endlocal & set "%2=%s%" & goto :eof
Below is a complete example that covers both disabled/enabled delayed expansion states, and also verifies the input/output values to match the expected test strings. Checked to run without errors in xp.sp3 and win7x64.sp1.
Code: Select all
@echo off & setlocal disableDelayedExpansion
for /f "usebackq tokens=1* delims= " %%a in ("%~f0") do (
@rem read test strings off ::: comment lines at the bottom
if "%%~a"==":::" set "u=%%~b" & call :test u
)
endlocal & goto :eof
:test [in,ref] str
setlocal disableDelayedExpansion
setlocal enableDelayedExpansion & set "v=!%1:%%=%%%%!"
endlocal & set "v=%v%"
call :copy.ddx "%v%" w u
endlocal
setlocal enableDelayedExpansion
set "v=!%1:%%=%%%%!"
call :copy.edx "!v!" w u
endlocal
goto :eof
:copy.ddx [in,val] src [out,ref] dst [in,ref] src [ret] errorlevel _______
setlocal disableDelayedExpansion
set "s=%~1"
set "s=%s:^^=^%"
call :comp s %3 %0 "- arg err" || (endlocal & set "%2=" & exit /b 1)
@rem argument verified ok, a real function would now process %s% further
endlocal & set "%2=%s%"
@rem dummy copy function expected to return identical string as inputted
call :comp %2 %3 "" "- ret err"
exit /b %errorlevel%
:copy.edx [in,val] src [out,ref] dst [in,ref] src [ret] errorlevel _______
setlocal disableDelayedExpansion
set "s=%~1"
set "s=%s:^^=^%"
call :comp s %3 %0 "- arg err" || (endlocal & set "%2=" & exit /b 1)
@rem argument verified ok, a real function would now process %s% further
if "%s%" equ "%s:!=%" goto :copy.edx.ret
set "s=%s:^=^^%"
set "s=%s:!=^!%"
:copy.edx.ret
endlocal & set "%2=%s%"
@rem dummy copy function expected to return identical string as inputted
call :comp %2 %3 "" "- ret err"
exit /b %errorlevel%
:comp [in,ref] src [in,ref] dst [in,val] tag [in,val] error-tag __________
setlocal enableDelayedExpansion
if "%~3" neq "" echo %~3 "!%1!"
if "!%1!" neq "!%2!" echo %~4 "!%2!" & endlocal & exit /b 1
endlocal & exit /b 0
:: strings are being read literally so ^%!(&)<|> need not be escaped ________
::
::: c:\windows\temp\ntfs.ads:1
::: \\localhost\c$\windows\temp\.\(a&b%!*.??^
::: cmd /c echo /? 1>&2
::: * *. .* *.* ? ?. .? ?.? ?.? . .. ... ^ ^^ ^^^
::: & ^& ^^& | ^| ^^| < ^< ^^< > ^> ^^>
::: & && &&& | || ||| < << <<< > >> >>>
::: , ,, ,,, ; ;; ;;; ' '' ''' ` `` ``` ~ ~~ ~~~
::: + ++ +++ - -- --- / // /// \ \\ \\\ = == ===
::: ( ^( ^^( % %% %%% %%%% %cd% %%cd%% %%%%cd%%%%
::: ) ^) ^^) ! !! !!! !!!! !cd! !!cd!! !!!!cd!!!!
::: ) ^) ^^)
::: ) ^) ^^) !
::: ! ^! ^^! !! !^! ^!! ^!^!
::: !cd! !cd^! ^!cd! ^!cd^!
::: !!cd!! !!cd!^! !!cd^!! !!cd^!^! !^!cd!! !^!cd!^! !^!cd^!! !^!cd^!^!
::: ^!!cd!! ^!!cd!^! ^!!cd^!! ^!!cd^!^! ^!^!cd!! ^!^!cd!^! ^!^!cd^!! ^!^!cd^!^!
::: ) !
::: ) ^!
::: ^) !
::: ^) ^!
::: ^!
::: ^!^!
::: ^!cd^!
::: ^!^!cd^!^!
::: ) ^!
::: ) ^^!
::: ^) ^!
::: ^) ^^!
::: ^ ^^ ^^^ ^^^^ ^^^^^ ^^^^^^ ^^^^^^^^ ^^^^^^^^^
The code is written for readability, rather than real life performance, so I am not looking for tricks to eliminate the 'goto' in :call.edx for example, yet any critique is welcome about additional/failure test cases, or simplifying the logic, or different approaches.
Liviu