ECHO命令輸出空行的11種方法和效率

echo是批處理中最簡單的命令,可是你真的掌握了嗎?你知道echo輸出空行的十種方法嗎?你知道用echo怎麼輸出on或者off或者/?嗎?你知道echo, echo+ echo.哪一個效率更高嗎?php

 

衆所周知,若是echo後面跟一個環境變量,可是該變量卻爲空時,至關於不加任何參數的echo,即輸出當前echo是on仍是off。不少文章或者教程給出的解決方案都是在echo後面加一個點號echo.,這樣就會輸出空行。html

@echo off
echo %demon.tw%
:: ECHO is off.
echo.%demon.tw%
pause

據我所知,用echo輸出空行至少有十種方法:java

@echo off

echo=
echo,
echo;

echo+
echo/
echo[
echo]

echo:
echo.
echo\

pause

這十種方法能夠分爲三組,每組的效率依次遞減。可悲的是,那些被奉爲經典的教程給出的倒是效率最低那組中的echo.nginx

echo.不只效率低下,並且還容易引起錯誤:windows

@echo off
cd .>echo
echo.
pause

我知道你很難接受,但事實的確如此。dom

第一組中echo後面的=,;都是批處理中的分隔符,因此CMD能夠正確地解析出echo命令,並把=,;做爲echo命令的參數。是的,你沒有看錯,分隔符並非用來分隔命令與參數,它們一般是參數的一部分。既然是參數,那麼爲何不會被輸出?那是由於echo命令直接跳過了參數的第一個字符,從第二個字符開始輸出,而第二個字符是NUL,因此輸出了空行。ide

你可能又要問,那爲何用空格作分隔符卻不能輸出空行呢?那是由於在輸出以前,CMD要檢查echo命令的參數是否是on或者off,或者參數爲空:首先跳過全部空白字符,若是跳過以後字符串就結束了,那麼就認爲沒有加參數,輸出echo是on仍是off;若是字符串沒有結束,就調用wcsnicmp函數來判斷剩下的字符串是否爲on或者off,進而修改echo的狀態。函數

所以加上不少空格也是同樣的效果:post

@echo         off
echo             
echo           on
echo             
pause

而對於第二和第三組,事情就沒那麼簡單了,因爲echo後面跟的並非分隔符,因此解析以後會被當成一個總體,而echo+ echo/等等顯然又不是內部命令,CMD會把它們當作外部命令進行搜索。嗯,你知道,搜索是很花時間的,這就是爲何它們的效率低於第一組。

惋惜的是,CMD花了很大力氣搜索,卻仍然找不到這樣的外部命令,這時候它會嘗試着修復(Fix)命令,看看命令中是否有某些字符(如圖):

FindAndFix

能夠看到,CMD對:.\的處理跟+[]/不太同樣,若是是+[]/,CMD會直接把它們從命令中刪除而且添加到原有參數的前面;而若是是:.\而且CMD拓展是開啓的話,那麼會多調用一次GetFileAttributes函數獲取文件屬性,多調用一次函數天然會多花一些時間,因此第三組的效率又稍稍比第二組的低些。

GetFileAttributes

再來解釋一下爲何echo.有時候會引發錯誤。文件名中是不能出現:.\的,理論上GetFileAttributes函數都應該返回-1(INVALID_FILE_ATTRIBUTES),然而事實卻不是如此,我也不知道這算不算GetFileAttributes函數的BUG:

#include <stdio.h>
#include <windows.h>

int main()
{
    FILE *fp = fopen("echo", "wb");
    fclose(fp);
    printf("0x%x\n", GetFileAttributes("echo:"));
    printf("0x%x\n", GetFileAttributes("echo."));
    printf("0x%x\n", GetFileAttributes("echo/"));
    return 0;
}

若是你測試一下上面的C程序,就會發現echo.那行返回的不是-1。

若是GetFileAttributes函數返回的不是-1(通常表示文件不存在),也不是0x10(表示文件是文件夾),那麼命令仍是會保持原來的樣子,當成外部命令運行。

@echo off
cd .>echo
echo.
pause

‘echo.’ is not recognized as an internal or external command, operable program or batch file.

@echo off
cd .>echo
setlocal disableextensions
echo.
pause

關閉了CMD拓展,沒有問題。

@echo off
md echo
echo.
pause

echo是文件夾而不是文件,沒有問題。

最後總結一下吧,在大部分狀況下,你都應該使用第一組的echo, echo; echo=來進行輸出,它們的效率跟echo (空格)是同樣的,而且能夠用來輸出on或者off,在變量爲空時還能輸出空行。

可是echo, echo; echo=卻不能輸出以/?開頭的行,若是你須要,可使用第二組的echo+ echo/ echo[ echo],它們的效率低一些,但能保證原樣輸出。

我不建議你使用第三組的echo: echo. echo\,若是你仍然要像垃圾教程裏面那樣用,我也沒有辦法。

因爲當時沒有分析文件搜索的CALL(太複雜懶得跟蹤),錯誤的認爲它們的搜索過程都是同樣的,在簡單分析了一下分析文件搜索的過程以後,發現有一些觀點是錯誤的,現予以糾正。

CMD在進行外部命令搜索時,若是命令中存在冒號:或者反斜杆\,處理的方法與不存在時是不同的。另外,在搜索開始以前斜杆/(即Unix路徑分隔符)會被替換成反斜槓\,故斜杆和反斜杆效果是同樣的。

具體的處理過程比較複雜,就不展開了,具體到echo而言,echo/ echo: echo\都不會進行實際的文件搜索,只是會調用一些無關痛癢的函數,對效率的影響基本是能夠忽略的。

而echo+ echo[ echo] echo.會對工做目錄與%PATH%中的目錄進行搜索,速度天然會比較慢。

因此按照效率高低排列的話,正確的分組應該是:

@echo off

echo=
echo,
echo;

echo/
echo:
echo\

echo+
echo[
echo]
echo.

pause

固然,組員之間可能還有細微的差異。好比拓展開啓的話,第二組echo\會比echo/多調用一次GetFileAttributes(上面有談到);第三組的echo.也許還會比其餘組員更慢一點(沒有驗證,實在懶得分析了),這些幾乎是能夠忽略不計的。

最後,輸出空行其實還有第十一種方法,這彷佛的確是通用性最強並且效率也很高的方法。

setlocal enabledelayedexpansion
echo(
echo(/?
echo(on
echo(off
echo(!tmp:\=!

能夠用下面的VBS測試效率:

'Author: Demon
'Website: http://demon.tw

Set fso = CreateObject("scripting.filesystemobject")
set WshShell = CreateObject("wscript.Shell")

s = "(=,;/\:+[]."

For i = 1 To Len(s)
    c = Mid(s, i, 1)
    h = Hex(Asc(c))
    
    With fso.OpenTextFile(h & ".bat", 2, True)
        .WriteLine "@echo off"
        .WriteLine "set s=%time%"
        For j = 1 To 100
            .WriteLine "echo" & c '& ">nul"
        Next
        .WriteLine "set e=%time%"
        .Write "echo echo" & c & " %s% %e%>" & h & ".txt"
    End With
    
    WshShell.Run h & ".bat", 0, True
    
    With fso.OpenTextFile(h & ".txt")
        a = Split(.ReadLine, " ")
    End With
    With fso.OpenTextFile("echoLog.txt",8,true)
        .WriteLine  a(0) & "  " & TimeDiff(a(1), a(2))
    End With
    
    WScript.Echo a(0), TimeDiff(a(1), a(2))
    
    fso.DeleteFile h & ".bat"
    fso.DeleteFile h & ".txt"
Next

Function TimeDiff(s, e)
    t = DateDiff("s", CDate(Left(s, 8)), CDate(Left(e, 8)))
    t = t * 1000 + (Right(e, 2) - Right(s, 2)) * 10
    TimeDiff = t
End Function

拓展閱讀:http://bbs.bathome.net/viewthread.php?tid=18350

隨機文章:

  1. 使用Windows Script Encoder加密VBS
  2. 用VBS實現PHP的md5_file函數
  3. JavaScript 記憶(Memoization)
  4. 隱藏Nginx和PHP版本號
  5. OpenWrt配置IPv6之6to4隧道

 

出處:http://demon.tw/reverse/cmd-internal-echo.html

相關文章
相關標籤/搜索