使环境变量在ENDLOCAL中生存 [英] Make an environment variable survive ENDLOCAL

查看:172
本文介绍了使环境变量在ENDLOCAL中生存的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个批处理文件,通过一系列中间变量计算变量:

  @echo off 
setlocal

set base = compute directory
set pkg = compute子目录
set scripts =%base%\%pkg%\Scripts

endlocal

%scripts%\activate.bat

最后一行没有被调用,因为它是在endlocal之后的,它解决了脚本环境变量,但是它必须来自endlocal,因为它的目的是设置一堆



如何调用脚本谁的目的是设置永久环境变量,但谁的位置是由一个临时的环境变量?



我知道我可以在endlocal之前创建一个临时批处理文件,并在endlocal之后调用它,如果没有其他的话,但我想知道是否有一个les

解决方案

ENDLOCAL& SET VAR =%TEMPVAR%模式是经典的。但是有些情况并不理想。



如果您不知道TEMPVAR的内容,那么如果值包含特殊字符,例如< > & | 。通常可以使用 SETVAR =%TEMPVAR%之类的引号来保护它,但如果有特殊字符并且该值已经被引用,那么可能会导致问题。 / p>

如果您关心特殊字符,FOR表达式是跨越ENDLOCAL屏障传输值的绝佳选择。延迟扩展应在ENDLOCAL之前启用,并在ENDLOCAL之后被禁用。

  setlocal enableDelayedExpansion 
setTEMPVAR = This &那个^&另一件事
for / fdelims =%% A in(!TEMPVAR!)do endlocal& setVAR = %%〜A

限制:




  • 在ENDLOCAL之后启用,则如果TEMPVAR包含,则最终值将被破坏。


  • 包含一个lineFeed字符的值不能被传输




如果你必须返回多个值,而且你知道一个不能出现在任何一个值,然后只需使用适当的FOR / F选项。例如,如果我知道值不能包含 |

  setlocal enableDelayedExpansion 
settemp1 = val1
settemp2 = val2
for / ftokens = 1,2 del $ = $$$$
= %%〜B

如果您必须返回多个值,设置是无限制的,那么你嵌套的FOR / F循环:

  setlocal enableDelayedExpansion 
settemp1 = val1
settemp2 =$
for / fdelims =%% A in(!temp1!)do(
for / fdelims =%% B in(! )do(
endlocal
setvar1 = %%〜A
setvar2 = %%〜B


绝对查看 jeb的答案,用于安全,防弹技术,适用于所有情况下的所有可能值。



2017-08-21 - 新功能RETURN.BAT

我已经与DosTips用户jeb一起开发一个名为RETURN.BAT的批处理实用程序,可用于退出脚本或调用例程并返回一个或多个变量ENDLOCAL屏障。很酷: - )



以下是3.0版的代码。我很可能不会保持这个代码是最新的。



RETURN.BAT

  :: RETURN.BAT版本3.0 
@if%〜2equ(goto:return.special)else goto :return
:::
::: call RETURN ValueVar ReturnVar [ErrorCode]
:::由批处理函数用于EXIT / B,并安全地返回
之间的任何值: :: ENDLOCAL屏障。
::: ValueVar =包含返回值的局部变量的名称。
::: ReturnVar =接收返回值的变量的名称。
::: ErrorCode =返回的ERRORLEVEL,如果未指定则默认为0。
:::
::: call RETURNValueVar1 ValueVar2 ...ReturnVar1 ReturnVar2 ...[ErrorCode]
:::与之前相同,除了第一个和第二个被引用和空格
:::分隔的变量名列表。
:::
:::注意,所有赋值(变量名和值)
:::的总长度必须小于3.8k字节。不执行任何检查来验证所有
:::作业是否符合限制。变量名不能包含空格,
::: tab,逗号,分号,插入符号,星号,问号或感叹号。
:::
:::调用RETURN init
:::定义return.LF和return.CR变量。不需要,但应该是
:::在脚本的顶部调用一次,以提高RETURN的性能。
:::
::: return /?
:::显示此帮助
:::
::: return / V
:::显示版本RETURN.BAT
:::
:::
::: RETURN.BAT由Dave Benham和DosTips用户jeb编写,最初是
:::发布在后面的DosTips线程中:
::: http ://www.dostips.com/forum/viewtopic.php?f = 3& t = 6496
:::
:: =============== ================================================== =============
::如果以下代码在脚本中复制,则可以删除:return.special代码
::,您的脚本可以使用以下调用:
::
:: call:return ValueVar ReturnVar [ErrorCode]
::
:: call:return.init
::

:return ValueVar ReturnVar [ErrorCode]
::安全地返回ENDLOCAL屏障之间的任何值。默认ErrorCode为0
setlocal enableDelayedExpansion
如果未定义return.LF调用:return.init
如果未定义return.CR调用:return.init
setreturn.normalCmd =
setreturn.delayedCmd =
setreturn.vars =%〜2
for %% a in(%〜1)do for / ftokens = 1 * %% b in(!return.vars!)do(
setreturn.normal =!%% a!
如果定义return.normal(
setreturn.normal =!return.normal:%% = %% 3!
setreturn.normal =!return.normal:= %% 4!
for %% C in(!return LF!)设置return.normal =!return.normal:%%〜C = %%〜1!
for %% C in(!return.CR!)do setreturn。 normal =!return.normal:%%〜C = %% 2!
setreturn.delayed =!return.normal:^ = ^^^^!
)else setreturn。 delayed =
如果定义return.delayed调用:return.setDelayed
setreturn.normalCmd =!return.normalCmd!& set%% b =!return.normal!^!
setreturn.delayedCmd =!return.delayedCmd!& set%% b =!return.delayed!^!
setreturn.vars = %% c

seterr =%〜3
如果未定义err集合err = 0
for %% 1 in(!return.LF!)for for / f令牌= 1-3%% 2(^!return.CR! %%)do(
(goto)2> nul
(goto)2> nul
如果^!^equ^!(%return.delayedCmd:〜1 %)else%return.normalCmd:〜1%
如果%err%equ 0(call)else如果%err%equ 1(call)else cmd / c exit%err%


:return.setDelayed
setreturn.delayed =%return.delayed:!= ^^^!%!
退出/ b

:return.special
@if / i%〜1equinitgoto return.init
@if%〜1 equ/? (
for / ftokens = * delims =:%% A in('findstr^ :::%〜f0')do @echo(%% A
exit / b 0

@if / i%〜1equ/ V(
for / ftokens = * delims =:%% A in('findstr / rc: ^ :: RETURN.BAT Version%〜f0')do @echo %% A
exit / b 0

@>& 2 echo ERROR:无效呼叫到RETURN.BAT
@exit / b 1


:return.init - 初始化return.LF和return.CR变量
set ^return.LF = ^

^上面的空行是至关重要的 - 请勿在/ f %% C中删除
('copy / z%〜f0nul')do setreturn .CR = %% C
退出/ b 0


I have a batch file that computes a variable via a series of intermediate variables:

@echo off
setlocal

set base=compute directory
set pkg=compute sub-directory
set scripts=%base%\%pkg%\Scripts

endlocal

%scripts%\activate.bat

The script on the last line isn't called, because it comes after endlocal, which clobbers the scripts environment variable, but it has to come after endlocal because its purpose is to set a bunch of other environment variables for use by the user.

How do I call a script who's purpose is to set permanent environment variables, but who's location is determined by a temporary environment variable?

I know I can create a temporary batch file before endlocal and call it after endlocal, which I will do if nothing else comes to light, but I would like to know if there is a less cringe-worthy solution.

解决方案

The ENDLOCAL & SET VAR=%TEMPVAR% pattern is classic. But there are situations where it is not ideal.

If you do not know the contents of TEMPVAR, then you might run into problems if the value contains special characters like < > & or|. You can generally protect against that by using quotes like SET "VAR=%TEMPVAR%", but that can cause problems if there are special characters and the value is already quoted.

A FOR expression is an excellent choice to transport a value across the ENDLOCAL barrier if you are concerned about special characters. Delayed expansion should be enabled before the ENDLOCAL, and disabled after the ENDLOCAL.

setlocal enableDelayedExpansion
set "TEMPVAR=This & "that ^& the other thing"
for /f "delims=" %%A in (""!TEMPVAR!"") do endlocal & set "VAR=%%~A"

Limitations:

  • If delayed expansion is enabled after the ENDLOCAL, then the final value will be corrupted if the TEMPVAR contained !.

  • values containing a lineFeed character cannot be transported

If you must return multiple values, and you know of a character that cannot appear in either value, then simply use the appropriate FOR /F options. For example, if I know that the values cannot contain |:

setlocal enableDelayedExpansion
set "temp1=val1"
set "temp2=val2"
for /f "tokens=1,2 delims=|" %%A in (""!temp1!"|"!temp2!"") do (
   endLocal
   set "var1=%%~A"
   set "var2=%%~B"
)

If you must return multiple values, and the character set is unrestricted, then use nested FOR /F loops:

setlocal enableDelayedExpansion
set "temp1=val1"
set "temp2=val2"
for /f "delims=" %%A in (""!temp1!"") do (
  for /f "delims=" %%B in (""!temp2!"") do (
    endlocal
    set "var1=%%~A"
    set "var2=%%~B"
  )
)

Definitely check out jeb's answer for a safe, bullet proof technique that works for all possible values in all situations.

2017-08-21 - New function RETURN.BAT
I've worked with DosTips user jeb to develop a batch utility called RETURN.BAT that can be used to exit from a script or called routine and return one or more variables across the ENDLOCAL barrier. Very cool :-)

Below is version 3.0 of the code. I most likely will not keep this code up-to-date. Best to follow the link to make sure you get the latest version, and to see some example usage.

RETURN.BAT

::RETURN.BAT Version 3.0
@if "%~2" equ "" (goto :return.special) else goto :return
:::
:::call RETURN  ValueVar  ReturnVar  [ErrorCode]
:::  Used by batch functions to EXIT /B and safely return any value across the
:::  ENDLOCAL barrier.
:::    ValueVar  = The name of the local variable containing the return value.
:::    ReturnVar = The name of the variable to receive the return value.
:::    ErrorCode = The returned ERRORLEVEL, defaults to 0 if not specified.
:::
:::call RETURN "ValueVar1 ValueVar2 ..." "ReturnVar1 ReturnVar2 ..." [ErrorCode]
:::  Same as before, except the first and second arugments are quoted and space
:::  delimited lists of variable names.
:::
:::  Note that the total length of all assignments (variable names and values)
:::  must be less then 3.8k bytes. No checks are performed to verify that all
:::  assignments fit within the limit. Variable names must not contain space,
:::  tab, comma, semicolon, caret, asterisk, question mark, or exclamation point.
:::
:::call RETURN  init
:::  Defines return.LF and return.CR variables. Not required, but should be
:::  called once at the top of your script to improve performance of RETURN.
:::
:::return /?
:::  Displays this help
:::
:::return /V
:::  Displays the version of RETURN.BAT
:::
:::
:::RETURN.BAT was written by Dave Benham and DosTips user jeb, and was originally
:::posted within the folloing DosTips thread:
:::  http://www.dostips.com/forum/viewtopic.php?f=3&t=6496
:::
::==============================================================================
::  If the code below is copied within a script, then the :return.special code
::  can be removed, and your script can use the following calls:
::
::    call :return   ValueVar  ReturnVar  [ErrorCode]
::
::    call :return.init
::

:return  ValueVar  ReturnVar  [ErrorCode]
:: Safely returns any value(s) across the ENDLOCAL barrier. Default ErrorCode is 0
setlocal enableDelayedExpansion
if not defined return.LF call :return.init
if not defined return.CR call :return.init
set "return.normalCmd="
set "return.delayedCmd="
set "return.vars=%~2"
for %%a in (%~1) do for /f "tokens=1*" %%b in ("!return.vars!") do (
  set "return.normal=!%%a!"
  if defined return.normal (
    set "return.normal=!return.normal:%%=%%3!"
    set "return.normal=!return.normal:"=%%4!"
    for %%C in ("!return.LF!") do set "return.normal=!return.normal:%%~C=%%~1!"
    for %%C in ("!return.CR!") do set "return.normal=!return.normal:%%~C=%%2!"
    set "return.delayed=!return.normal:^=^^^^!"
  ) else set "return.delayed="
  if defined return.delayed call :return.setDelayed
  set "return.normalCmd=!return.normalCmd!&set "%%b=!return.normal!"^!"
  set "return.delayedCmd=!return.delayedCmd!&set "%%b=!return.delayed!"^!"
  set "return.vars=%%c"
)
set "err=%~3"
if not defined err set "err=0"
for %%1 in ("!return.LF!") do for /f "tokens=1-3" %%2 in (^"!return.CR! %% "") do (
  (goto) 2>nul
  (goto) 2>nul
  if "^!^" equ "^!" (%return.delayedCmd:~1%) else %return.normalCmd:~1%
  if %err% equ 0 (call ) else if %err% equ 1 (call) else cmd /c exit %err%
)

:return.setDelayed
set "return.delayed=%return.delayed:!=^^^!%" !
exit /b

:return.special
@if /i "%~1" equ "init" goto return.init
@if "%~1" equ "/?" (
  for /f "tokens=* delims=:" %%A in ('findstr "^:::" "%~f0"') do @echo(%%A
  exit /b 0
)
@if /i "%~1" equ "/V" (
  for /f "tokens=* delims=:" %%A in ('findstr /rc:"^::RETURN.BAT Version" "%~f0"') do @echo %%A
  exit /b 0
)
@>&2 echo ERROR: Invalid call to RETURN.BAT
@exit /b 1


:return.init  -  Initializes the return.LF and return.CR variables
set ^"return.LF=^

^" The empty line above is critical - DO NOT REMOVE
for /f %%C in ('copy /z "%~f0" nul') do set "return.CR=%%C"
exit /b 0

这篇关于使环境变量在ENDLOCAL中生存的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆