今天寫了一個自動更新程序的批處理腳本,但是有個變量一直賦值有問題。弄了一個下午終於找到原因及解決方法:
----轉載要說明來自:博客園--邦邦醬好 哦
有問題的代碼如下:
@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的動態賦值過程中能夠被賦值。
同時注意開啟延遲本地環境變量擴展后,變量需要用!!進行引用。