跳到主要內容

發表文章

目前顯示的是有「Windows Batch」標籤的文章

Windows Batch - 清空檔案的方法

Problem 在Windows上如果採用砍掉重建或copy NUL的方法,有可能會因為檔案正在被使用而無法操作: copy / y NUL file.txt How to? 以下方式都是會清空,但最上方會有空白行: echo. > file.txt echo ( > file.txt echo [ > file.txt 完全清空可以透過以下幾種方法: break > file.txt type NUL > file.txt 但這些方法依然會受到檔案使用中的限制;最後發現可以透過poweshell去清空內容,搭配batch使用的方法: powershell.exe -ExecutionPolicy Bypass -Command "Clear-Content file.txt" Reference How to delete content from a text file using windows batch script? Clear contents of a locked file in windows call powershell of a batch file

Windows Batch - 管理事件檢視器

WEVTUTIL 取得遠端機器log 加上/r:ip、/u:domain\username與/p:passwd: C:\Windows\system32 > wevtutil qe Application / c: 1 / rd:true / f:text / r:10.134.1431 / u:administrator / p: 12345 Event [ 0 ] : Log Name: Application Source: SuperMicro Health Assistant Date: 2015 -05-18T07: 11 : 49.000 Event ID: 109 Task: N / A Level: Warning Opcode: Info Keyword: Classic User: N / A User Name: N / A Computer: ssmlab2 Description: uuid:94faf3e1-1d3a-421a-b048-7b5ae8203bb3,index: 23 total: 27 ,monitored: 20 ,warn: 1 2015 -05- 17 23 : 11 : 49 WARNING Power1 exceeds high limit . 根據時間做Query 首先要將搜尋時間切換成UTC時間。以下面命令為例,要搜尋application種類中,Event建立時間為2015-05-19T00:00:00~2015-05-20T00:00:00間: wevtutil qe application / f:text / c: 1 "/q:*[System[TimeCreated[@SystemTime>='2015-05-19T00:00:00' and @SystemTime<='2015-05-20T00:00:00']]]" Reference WEVTUTIL-管理事件檢視器的工具

Windows Batch - 輸出的重新導向

會有輸出重新導向的需求,可能是因為我們想把執行指令的結果存到檔案內。做法相當簡單,只要使用>符號: command > file 如果要append在file後,就使用»: command >> file 但輸出流分成標準輸出stdout與標準錯誤輸出stderr,預設情況下都是操作sdtout。如果要輸出的是標準錯誤輸出: command 2 > file command 2 >> file 如果stdout與stderr要一同輸出,可以這樣搞,代表要將sdterr導入stdout中: command > file 2 >& 1 如果今天你要執行一堆command,而且希望都輸出到檔案內呢? 可以透過Block的方式: > file 2 >& 1 ( command1 command2 command3 ) 切記,如果有做變數修改的動作,要把它放到Block之前,否則設置是無用的。另外一個方法是透過Jump的方式: call :redirect > file 2 >& 1 exit / b   :redirect command1 command2 command3 exit / b Jump的方式不會造成設置變數的問題,但必須小心流程的控制。像在call :redirect後,必須執行exit,也可以在這再call另外一個Jump到最後面的label,避免繼續往下重複執行。 友藏內心獨白: 要寫好看的Batch需要懂非常多東西啊! Reference redirect-stdout-and-stderr-from-inside-a-batch-file Redirection STDIN、STDOUT、STDERR 與 Bash Redirection 測試

Windows Batch - 管理系統服務

 Normal Operation REM 停止服務 sc stop % SERVICE % REM 啟動服務 sc start % SERVICE % REM 查詢服務 sc query % SERVICE % Kernel Service Create 腳本接受兩個參數:(1)服務名稱;(2)驅動程式完整路徑。流程就是針對輸入參數做檢查,接著確認是否為已加服務,已加就不做,沒加就做。而透過sc query確認服務是否存在的方法,原先確認服務是否存在是透過%errorlevel%是否為1060,但在XP與2003上抓不到正確的%errorlevel%,因此改透過確認服務名稱字串的方式做判斷。另外有特別處理的部分是Driver的路徑,因為有可能路徑包含空格,而造成User在傳入參數時加上雙引號。為了避免判斷或建立服務時不好處理,在一開始就做了字串取代把雙引號給變成空字串。 @echo off set SERVICE=%1 set BINPATH=%2 set BINPATH=%BINPATH:"=% if "%SERVICE%" == "" ( echo Please enter the service name. exit /b 2 ) if "%BINPATH%" == "" ( echo Please enter the driver path. exit /b 2 ) if not exist "%BINPATH%" ( echo Driver path[%BINPATH%] is incorrect! exit /b 2 ) sc query %SERVICE% | findstr %SERVICE% > NUL if errorlevel 1 ( sc create %SERVICE% binPath= "%BINPATH%" type= kernel start= auto error= normal ) else ( echo Service [%SERVICE%] exists! ) exit /b %errorlevel% 使用範例: installDriver.bat mydrive

Windows Batch - 修改環境變數

Introduction 操作環境變數是常遇到的議題。像安裝軟體時,為了要讓使用者可以在command line直接執行我們的程式,我們會將程式資料夾路徑加入到PATH變數中;移除軟體時就會把它從PATH中拿掉。或者我們有某些如軟體安裝目錄的資訊,希望能夠讓我們不同的程式得知,而將這個值建立到環境變數中以方便操作。接下來將針對操作環境變數的不同方法做介紹。 Methods registry 輸入regedit打開註冊檔編輯器。修改目前使用者環境變數: HKEY_CURRENT_USER\Environment 修改系統環境變數: KEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment Command line: REM 設定環境變數TEST為 % PATH % 的內容 reg add "HKEY_CURRENT_USER/Environment" / v TEST / t REG_EXPAND_SZ / d "%PATH%" / f REM 設定環境變數TEST為 % PATH % reg add "HKEY_CURRENT_USER/Environment" / v TEST / t REG_EXPAND_SZ / d ^ % PATH^ % / f REM 刪除環境變數TEST reg delete "HKEY_CURRENT_USER/Environment" / v TEST / f 修改後不會立即生效。 setx.exe 微軟提供修改環境變數的程式。可以直接參考命令說明: link 。修改完後效果與直接修改系統>進階系統設定>系統環境變數的效果相同。 wmic 操作WMI介面的Command line工具。根據網路上人家所說使用這個方法可以讓各個正在執行的程式立即取到最新的環境變數,但經過我測試僅WinXP可以,其餘是不行的,除非也是透過wmi去取得環境變數內容。(測試過XP, 2003, 2008, Win7, 2012) Create 建立變數%VAR%值%VALUE%到系統環境變數中: wmic ENVIRONMENT create name =

Windows Batch - 找不到的msg.exe - 神奇的sysnative

Problem 某天遇到windows command - msg.exe在win7/win8找不到的問題。這問題乍看之下非常的神奇,兩套不同的軟體但一個能夠使用一個則不行。在不行的那個軟體中加入Debug訊息,卻出現msg.exe找不到的怪異現象。這兩套軟體除了實作語言不同外,另外一個是32與64 bit之差別。執行的Command一樣,但為什麼32-bit的卻不能用呢? Root Cause 首先我透過了32與64位元的cmd.exe去執行msg.exe: (system32 for 32-bit;syswow64 for 64-bit) 可以發現在32位元的情況下根本找不到msg.exe,接著到C:/windows下搜尋msg.exe,確認system32與syswow64中是否都有msg.exe。事實是: 僅system32中有msg.exe。 這是系統限制,issue可不解嗎? 但問題是另外一個軟體做的到阿! 把一個軟體從32變成64位元是較難些,還是看看要如何用較簡單的方式去解決這個問題吧! How to ? 我試過直接呼叫%windir%\system32\msg.exe,也確認過%PATH%有指到system32,但事實上32-bit的service是無法直接存取system32的。還好這時候看到個一個叫sysnative的東西,它可以透過這個資料夾讓32的應用程式可以訪問到system32中的東西。 Batch 可以根據執行一次msg.exe去確認exit code,若非0代表有問題則改用sysnative的msg.exe: @ echo off set CMD =msg.exe   where % CMD % > NUL 2 >& 1   if not % errorlevel % == 0 ( set CMD = % windir % \sysnative\msg.exe )   % CMD % 也可以透過判斷sysnative中的msg.exe是否存在去決定要執行的Command: @ echo off if exist "%windir%\sysnative\msg.exe" ( set CMD = "%windir%\sysnative\msg.ex

Windows Batch - 修改正在執行的Batch

Problem 這陣子寫了隻外掛程式,希望能簡單的透過batch做線上更新。在執行batch去解壓縮並覆蓋所有內容後(當然包含更新batch本身),batch並沒正常的執行完成。以下是我的script: rem 前略..   "%ZIP_CMD%" x -y -o "%TEMP_DIR%" Bot.zip xcopy / i / e / y "%TEMP_DIR%"   del / f Bot.zip exit 0 在複製檔案(xcopy)後,並沒執行刪除動作。也許你會說: 那幹嘛更新這隻batch? 更新batch也是可能有變動的,我又懶得寫另外一隻程式替我處理更新問題。之前曾做過修改batch中jvm的功能,也有遇到 Root Cause 正在執行的batch被修改後,非常有可能會造成接下的執行不正常。我假設「Batch是做到哪裡讀到哪裡的行為」,於是我寫了兩隻測試Batch來替我驗證這假設是否成立。 test.bat: 複製test_tmp.bat取代自己。 test_tmp.bat: 一堆echo。 test.bat: @ echo off   set BASE = % ~dp0   cp -f "%BASE%test_tmp.bat" "%BASE%test.bat" test_tmp.bat: @ echo off   set BASE = % ~dp0 echo long~long~long~long~long~long~long~long~long~long~string echo long~long~long~long~long~long~long~long~long~long~string   echo 123 echo 456 先看看執行結果在做解釋: cp -f "%BASE%test_tmp.bat" "%BASE%test.bat" echo long~long~long~long~long~long~long~long~long~long~string 從test.bat與test_tmp.bat的第三行做比較可以驗證我的假設: 「Batch以行為單位,讀

Windows Batch - 等待網路連線

Problem 這故事是發生在重新開機後,Windows啟動網卡的速度實在太慢,造成ant的get task發生問題。已試過設定retries property,但無法解決問題。因此改透過batch的方式,確認網路連線正常後才去執行我們要執行的工作。 How to? 直覺的方法就是透過ping指令,並確認目標主機可以連線即可。為了避免語系問題,一開始我就使用chcp將語系切成英文。 chcp 437 set CHECK_HOST =www.yahoo.com.tw ping % CHECK_HOST % -w 1000 -n 2 | find "Reply from" 接著透過for迴圈與label做結果確認與Retry,當然Retry是需要最大限度的,直接看看完整的batch吧! @echo off chcp 437 set CHECK_HOST=tonylin.idv.tw set CMD_CHECK_HOST=ping %CHECK_HOST% -w 1000 -n 2 ^^^| find "Reply from" set MAX_TIMES=5 set /a "COUNT=0" :CHECK_CONNECTION for /f "usebackq tokens=*" %%r in (`%CMD_CHECK_HOST%`) do ( goto :DO_JOB ) set /a "COUNT = COUNT + 1" if "%COUNT%" == "%MAX_TIMES%" ( echo Retry a connection to %CHECK_HOST% failed. Force to do the job. goto :EXIT ) echo Retry a connection to %CHECK_HOST% %COUNT% time(s) timeout 1 > NUL goto :CHECK_CONNECTION :DO_JOB echo DONE! :EXIT 我設定MAX_TIMES為最大限制次數,COUNT為重試次數。在每次執行失敗後,COUNT都會加一,直它

Windows Batch - 找尋被占用的port

Problem 這問題發生在想執行某些程式卻發生port占用的情形,又不曉得兇手是誰。 How to? 其實透過netstat就可以解決這個問題了。首先是透過-b的參數: netstat -nab TCP [ :: ] : 49153 [ :: ] : 0 LISTENING eventlog [ svchost.exe ] TCP [ :: ] : 49154 [ :: ] : 0 LISTENING Schedule [ svchost.exe ] TCP [ :: ] : 49155 [ :: ] : 0 LISTENING [ lsass.exe ] TCP [ :: ] : 49168 [ :: ] : 0 LISTENING [ services.exe ] -b參數會顯示出占用的process,但如果加上findstr :port去過濾,會因跳行的關係無法看到兇手是誰。 因此有另外一個方法是透過-o加tasklist指令: netstat -nao | findstr : 80 TCP 0.0.0.0: 80 0.0.0.0: 0 LISTENING 5932 看到pid後,再使用tasklist找process名稱: tasklist | findstr 5932 Skype.exe 5932 Console 1 184,628 K 使用方法二的目的是為了讓你的batch比較好寫。 友藏內心獨白: 程式應該要寫得越簡單越好!

Windows Batch - Disable IE Security設定

Problem Server版本的Windows IE預設包含IE Security/Protected Mode的功能,瀏覽網頁時必須將目標URL設定為安全URL才能正常存取。這導致每次部屬測試環境時,都要花額外的時間去處理,因此我研究該如何透過script去將它給disable,以節省時間。 How to? Windows2008我透過 Process Monitor 去找到在操作IE Disable Protected Mode時,所需要修改的Registry。但這方法並不適用於Windows2003,必須改透過SYSOCMGR.EXE將IE Security Component移除。在實做上,首先可以透過ver指令判斷作業系統去決定要執行的動作。 ver | findstr "5\." > nul if not errorlevel 1 goto DISABLE_IE_SECURITY_ON_WIN2003   ver | findstr "6\." > nul if not errorlevel 1 goto DISABLE_PROTECTED_MODE_ON_WIN2008   echo Don 't support to disable the IE protected mode for the Windows. ver GOTO FINISH Win2003的部分我產生一個Config暫存檔,去餵給SYSOCMGR.EXE。其中IEHardenUser與IEHardenAdmin為針對User與Admin的Component,設為Off就會移除,反之設為On就會安裝。完成後會再把暫存檔刪除。 :DISABLE_IE_SECURITY_ON_WIN2003 echo Disable the IE Security set TMPFILE = "%BASE%IE-Security.tmp"   echo [ Components ] > % TMPFILE % echo IEHardenUser =Off >> % TMPFILE % echo IEHardenAdmin =Off >> % TMPF

Windows Batch - 在作業系統Shutdown時執行特定程式

Introduction 先前曾教大家如何 在作業系統啟動時執行特定程式 。當然!有開機執行相對的就有關機執行。首先確認了工作排程器有支援Event觸發的方式,接著確認如何辨別Shutdown事件。我透過了shutdown /s /t 100做Shutdown,並在事件檢視器中發現shutdown /s與shutdown /r都為id=1074的系統事件,也從MSDN中做了再次確認。(請用shutdown /a取消關機動作) 接下來就告訴大家如何透過batch去實做。 Just Do It! 與先前再啟動執行不同的是,/SC改透過ONEVENT。另外也會透過/EC與/MO參數分別指定事件通道與過濾的事件內容。 @echo off set RUN_TASK="%1" if %RUN_TASK% == "" goto emptyError set TASK_NAME=%~nx1 schtasks /Create /RL HIGHEST /RU SYSTEM /TN %TASK_NAME% /SC ONEVENT /TR %RUN_TASK% /EC SYSTEM /MO *[System/EventID=1074] goto :finish :emptyError echo Please enter a valid cmd goto :finish :finish pause 你可以透過操作工作排程器的UI去決定/EC與/MO該如何丟。(工作排程器>建立工作>開始工作:事件發生時>自訂>新事件篩選器) 設定完再切換到XML看內容就可以知道了。 另外,我也從MSDN中發現某些參數在XP與2003之前是不支援的! 還好2003和XP已經要完全被EOL了。 友藏內心獨白: 為什麼會有需要開機與關機執行特定程式呢? 讓我們繼續看下去。 Reference TechNet - 排程文件 SCHTASKS MSDN - Creating a Task

Windows Batch - 在作業系統啟動時執行特定程式

Introduction 大家都知道要讓系統啟動時執行特定程式,最簡單的方法就是把那隻程式的link丟到啟動中。 也可以新增這隻程式的執行路徑到HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run。 上述兩種方法,都必須登入後才會執行,當然我是可以讓它auto-login的,但我想這通常只會用於個人或測試環境。如果想要在系統啟動後就執行,可以透過系統管理工具的工作排程器。 reg add HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run / v AUTO_TEST_BOOTSTRAP / t REG_SZ / d "%BASE%bootstrap.bat" / f Just Do It! 為了方便我寫了一隻Batch,讓我可以直接將某隻程式註冊為開機執行。 @echo off set RUN_TASK="%1" if %RUN_TASK% == "" goto emptyError set TASK_NAME=%~nx1 schtasks /Create /RU SYSTEM /RL HIGHEST /TN %TASK_NAME% /SC ONSTART /TR %RUN_TASK% goto :finish :emptyError echo Please enter a valid cmd goto :finish :finish pause 主要是透過schtasks去排程。使用/RU SYSTEM讓這隻程式是以SYSTEM身分去執行;使用/RL HIGHEST確保以最高權限執行;使用/SC ONSTART指定開機執行;而/TN與/TR分別為Task ID與執行程式路徑。執行後,應該可以在工作排程器中看到。如果重複註冊會詢問你是否要覆蓋。 如果有參數,我建議是寫一個Batch將參數都放在裡面,會比較單純。 另外實做了removeScheduleTask.bat,負責移除註冊的SheduledTask,與scheduleTaskOnStart.bat是一對。 @echo off set RUN_TASK="%1"

Windows Batch - 如何根據CommandLine去砍掉你要砍的行程?

源起 故事是這樣的: 有一個服務在軟體反安裝時,會將此服務停止並移除。然而,偶爾會發生此服務產生之行程,在軟體移除後依然殘留在系統中。 根據這個問題,有兩個可能: 1. 沒等待服務完全停止就進行移除動作,導致其產生之行程無法正常終止。 2. 這是服務本身的Bug。由於它屬於third-party的軟體,我們也無法去修正它。因此我使用了以下對應措施: 1. 等待服務停止。 2. 等待其產生之行程終止。 3. 再不終止我就讓它終止。 由於此服務產生之行程,並不一定只有我們軟體安裝,使用者也可能自行安裝。因此我們必須針對我們自己的行程去終止。一開始我採用了之前實作的 程式 ,根據它的執行路徑去找到我們所產生的行程並中止。然而,先前的方法並無法順利使用,似乎和行程產生的方式有關係。 好加在有找到WMIC的指令,讓我們能夠從WMI中取得行程的資訊,以達到根據Commmandline過濾行程的目的。但為了寫出這個Batch,可謂一波三折。 Just Do It! 我完成的Batch需要調整的變數為PROC_NAME與COND。P_NAME為行程名稱;COND為過濾CommandLine條件,可能是參數或執行路徑中的token。以我的範例來說,我想關閉eclipse.exe所啟動的javaw.exe程式。因此我的PROC_NAME=javaw.exe,COND=eclipse。 Batch內容主要分為兩個Block: 1. 等待行程結束,每檢查一次結果若行程存在就等待一秒再重試。當五次後,行程還沒結束就跳至Block2做Terminate動作。 2. 終結目標行程,透過WMI找到對應之行程並終結。 @echo off setlocal enabledelayedexpansion set PROC_NAME=javaw.exe set COND=eclipse set CMD_GET_PROC=wmic path win32_process get Processid^^,Caption^^,Commandline ^^^| findstr "^%PROC_NAME%" ^^^| findstr "%COND%" rem block 1, wait for the process done set /a "i = 1"