標籤:
cmd批處理逸出字元%的詳細解釋
在命令列中使用for時不需要雙%,這源於命令直譯器對命令列與批處理的處理方式不同。
1、%是個ESCAPE字元,通常將之譯為逸出字元,但也有更形象的譯名脫逸字元、逃逸字元等。也就是說%不僅僅將與其相關的特定字串轉義並替換為特定字串,而且自身也會被“脫逸”。而且類似於C語言中的逸出字元"\",雙%會轉義並脫逸為單%,四%則脫為雙%。
2、for本身是一個特殊的命令,類似於一個特化的命令直譯器,因為它的功能實現需要執行多條語句,因此它必須也具有對命令列(特指do後的命令列)分析處理的功能。而command/cmd實現for時自然會借用自身原有的命令列分析模組,因此for具有二級轉義的特性,for中do後的語句被分兩級分析和解釋,第一級在command/cmd讀入並解釋for命令列時,第二級在for讀入並解釋do命令時,它通常會對同一命令列的進行多次解釋。
然後,我們可以注意到,在do中使用命令列參數變數和環境變數時,不需要雙%,那是因為,這些變數在經過第一級轉義後,被替換成特定的不變的字串常量,參與for迴圈的所有執行過程;而替代變數則要求在執行(do後的子命令列中)過程中不斷的動態變化,而這個變化自然仍需要通過脫逸字元來實現,因此使用雙%就是成了必然的選擇。
另外,還需要注意到,在命令列中使用for時不需要雙%,這源於命令直譯器對命令列與批處理的處理方式不同。在早期的DOS版本中,%在命令列中不被視為逸出字元,所以不會被轉義和脫逸,所以當時無法在命令列直接引用環境變數。而使用for時,只需要一個%供for進行轉義和脫逸就夠了。在以後的命令直譯器版本中,加入了命令列轉義的支援(主要是環境變數的支援),但命令列for使用單%的傳統仍然保留了下來。
而 cmd中的變數延遲替換是屬於特殊的情況,但不違背以上的轉義原則,只是for中的環境變數不再是常量了。
案例:
我在自己的一個bat檔案,內容:
@echo off
if "%1" == "" (goto input_data) ELSE (goto input_var)
:input_data
set /p var=INPUT DATE(fmt:yyyyMMddHHmm):
goto start
:input_var
set /a var=%1
goto start
:start
cd %cd%
md ..\..\to-save\TKcard\%var%
mysql -P3302 -uroot -pdst72j$mq)c%%8 -h61.160.245.119 -N -s -e"SET SESSION group_concat_max_len = 99999999;SELECT CONCAT(‘mysqldump -h61.160.245.119 -P3302 -uroot
-pdst72j$mq)c%%%%8 --opt -n TKCard_cn_cn_db210060000 ‘,GROUP_CONCAT(t.TABLE_NAME SEPARATOR ‘ ‘),‘ >‘) FROM information_schema.TABLES t WHERE t.TABLE_SCHEMA =
‘TKCard_cn_cn_db210060000‘ AND (t.TABLE_NAME LIKE ‘dict_%%‘) AND t.TABLE_NAME NOT IN(‘dict_keyvalue‘,‘dict_server‘,‘dict_operator_config‘);" > tkcard_dict.bat
for /f "delims=] tokens=1" %%a in (tkcard_dict.bat) do echo %%a ..\..\to-save\TKcard\%var%\tkcard_dict.sql >tkcard_dict.bat
call tkcard_dict.bat
上邊功能是將遠程指定資料庫中的所有基礎資料匯出來。檔案中寫了一個mysql 串連資料庫的指令碼,並且在這個指令碼裡 通過“-e” 參數執行 匯出所有基礎資料表資料的指令碼代碼(即mysqldump 命令),結果沒有達到自己的預期效果,分析原因,操作內網資料庫就可以,把ip地址改為遠端資料庫地址就不行了,分析來分析去最後原來是這個 資料庫連接密碼問題,即密碼內網密碼沒有 特殊字元 % ,而遠端這個有%,結果就是這個% 沒有特殊轉義出的問題,上邊的腳步裡涉及2處相同的密碼,一處是 串連資料庫時要密碼,還一個是 對資料庫中匯出所有基礎資料時用的 mysqldump命令時也要再要一次相同的密碼,這個bat檔案一共2處密碼,而且都是一樣的,密碼裡又涉及%,結果我就是沒有對這2處% 進行特殊轉義出問題了。上邊的是已經正確轉義了,原來密碼為: dst72j$mq)c%8 , 第一處正確轉義是在%前再加一個%,第二處是需要%前加3個%。 由於文章開頭%轉義規則,2個%轉義一次為%,4個%轉義後為2個%,那這裡第一處要想正確串連資料庫就保證密碼為原始密碼即有自身的一個%,那就必須加一個%,那這樣第一處密碼裡就有2個%,當windows 的命令列 解譯器執行串連資料庫時會轉義這2個% 為一個%,剛好和原始密碼一樣,而上邊第二處密碼要想也保證和原始密碼一樣那也要多加%,至於加幾個,我們先來看看上邊第二處涉及的邏輯先理一下。上邊一個是串連資料庫,並且尾碼mysql 的參數命令-e 即 執行後邊的sql指令碼,即先串連資料庫然後執行後邊指令碼,結構為 mysql -P[大寫p為連接埠] -u[串連資料庫的帳號] -p[小寫p為串連資料庫的密碼] -h[資料庫ip地址] -N[不顯示列資訊] -s[一行一行輸出,中間有tab分隔] -e[執行mysql的sql語句,後跟雙引號,雙引號裡為sql指令碼代碼]"[雙引號裡為前邊-e後跟的sql指令碼-此省略下邊解釋]" >tkcard_dict.bat[上邊sql執行的結果定向到tkcard_dict.bat檔案中,由於tkcard_dict.bat前沒有路徑,那就是目前的目錄,如果沒有tkcard_dict.bat檔案就建立,有就覆蓋] for...[for迴圈開始執行tkcard_dict.bat檔案]。
這裡特別把上邊-e後的雙引號中的指令碼拿出來解釋: SET SESSION group_concat_max_len = 99999999;SELECT CONCAT(‘mysqldump -h61.160.245.119 -P3302 -uroot
-pdst72j$mq)c%%%%8 --opt -n TKCard_cn_cn_db210060000 ‘,GROUP_CONCAT(t.TABLE_NAME SEPARATOR ‘ ‘),‘ >‘) FROM information_schema.TABLES t WHERE t.TABLE_SCHEMA =
‘TKCard_cn_cn_db210060000‘ AND (t.TABLE_NAME LIKE ‘dict_%%‘) AND t.TABLE_NAME NOT IN(‘dict_keyvalue‘,‘dict_server‘,‘dict_operator_config‘);"
意思是先設定grop_concat函數本次查詢session的長度,然後 select concat(mysqldump代碼-是把代碼拼成字串常量此不會執行 , group_concat(把從information_schema.TABLES 表中按照where條件全查出來的dict基礎資料表名字查出來用一個空格間隔並且拼成一個字串)-整個group_concat 的結果作為前邊concat函數的一個參數 , ‘ >‘ )
可以看出-e後雙引號中的腳步 中那個 select 腳步功能主要就是 從 information_schema.TABLES(這個資料庫中的TABLES表中存放是這台mysql伺服器中除了information_schema外的所有其他資料庫名字,以及所有資料庫中所有表名字,具體點即TABLES表中TABLE_SCHEMA欄位存放是資料庫名字,TABLES表中TABLE_NAME欄位存放是每個資料庫中表的名字) 表中按照where條件中給定的資料庫名字和資料表名字2個條件查詢出基礎資料表的表名字,然後把查出的基礎資料表名字通過GROUP_CONCAT 函數用一個空格間隔並且拼成一個字串,然後再和 mysqldump命令的字串 以及 一個‘[有一個空格] >‘ 共三個參數 通過concat 函數 拼成一個最終的字串 ,然互連過“ > tkcard_dict.bat ” 重新導向到 tkcard_dict.bat檔案中,即 tkcard_dict.bat檔案中內容最終 是 : mysqldump -h61.160.245.119 -P3302 -uroot
-pdst72j$mq)c%%%%8 --opt -n TKCard_cn_cn_db210060000 基礎資料表1 基礎資料表2 基礎資料表3 ... >
至於for 後邊的代碼就是不太清楚了,估計就是執行上邊 新產生的 tkcard_dict.bat 檔案中的內容吧,而其內容剛好就是 一個mysqldump指令碼,所以就把這個mysqldump指令碼執行後的結果都匯入到 自定路徑下的tkcard_dict.sql 檔案中把。至於最後的最上邊bat代碼中的最後的那個 “>tkcard_dict.bat” 就不知道幹什麼用的了。
cmd批處理逸出字元%的詳細解釋