今天写了一个自动更新程序的批处理脚本,但是有个变量一直赋值有问题。弄了一个下午终于找到原因及解决方法:
----转载要说明来自:博客园--邦邦酱好 哦
有问题的代码如下:
@echo off echo. set choice=1 :::::::::::::::::::::::::::::::::::::::::::::: :::::::::::::::程序包上传FTP:::::::::::::::::: :::::::::::::::::::::::::::::::::::::::::::::: if "%choice%"=="1" ( echo 请确认你已经把程序包放到本地指定目录下(y/n): set /p yn= echo %yn% if /i "%yn%"=="n" ( set yn="" goto end ) echo 开始上传本地最新的程序包到服务器上... echo 上传完毕 ) :end set choice= echo 脚本终止!
连续运行4次,中间有2次说“ECHO处于关闭状态”,然后程序就判断失败了,这是为什么呢?
多次试验发现,如果语句set /p yn= 是在if语句外部,就不会发生这样的问题。
解决代码:
增加了setlocal enabledelayedexpansion,再改%yn%为!yn!。
@echo off&setlocal enabledelayedexpansion echo. set choice=1 :::::::::::::::::::::::::::::::::::::::::::::: :::::::::::::::程序包上传FTP:::::::::::::::::: :::::::::::::::::::::::::::::::::::::::::::::: if "%choice%"=="1" ( echo 请确认你已经把程序包放到本地指定目录下(y/n): set /p yn= echo !yn! if /i "!yn!"=="n" ( set yn= goto end ) echo 开始上传本地最新的程序包到服务器上... ::call Upload_Ftp.bat echo 上传完毕 ) :end echo 脚本终止!
查了一个下午,发现原因如下:
批处理有一种机制是变量延迟,比如
@echo off set a=4 set a=5&echo %a% pause
打印的结果会是a=4,而不是a=5,因为在读取了一条完整的语句(set a=4)之后,不立即对该行的变量赋值,而会在某个单条语句(set a=5&echo %a%)执行之前再进行赋值操作。也就是说“延迟”了对变量的赋值。
在cmd执行命令前会对脚本进行预处理,其中有一个过程是变量识别过程,在这个过程中,如果有两个%括起来的如%value%类似这样的变量,就会对其进行识别,并且查找这个变量对应的值,再而将值替换掉这个变量,这个替换值的过程,就叫做变量扩展,然后再执行命令。一般是对静态变量进行值的替换。
setlocal enabledelayedexpansion 就是延迟本地环境变量扩展,在cmd执行中,发生动态的一种情况是在 for语句,if语句中进行变量赋值,在动态变量赋值过程中,如果不主动开启延迟本地环境变量扩展,是不会对变量进行预处理的。
在上文第一段代码中,由于在语句(if /i "!yn!"=="n")之前没有对yn变量进行赋值,所以得到“ECHO处于关闭状态”的结果。
比如上方第三段代码,如果启动了变量延迟并把set a=5&echo%a%改为set a=5&echo!a!,所以批处理能够感知到动态变化,即不是先给该行变量赋值,而是在运行过程中给变量赋值,因此此时a的值就是5了。
所以我们就要在第一段代码中加入setlocal enabledelayedexpansion语句,开启延迟本地环境变量扩展,使得yn变量在if的动态赋值过程中能够被赋值。
同时注意开启延迟本地环境变量扩展后,变量需要用!!进行引用。