批處理學(xué)習(xí):查找文件夾下非指定拓展名的文件

需求

在Windows系統(tǒng)中主儡,以命令行方式奖唯,找出當(dāng)前目錄下所有拓展名不是.md.js糜值、.bat的文件丰捷。

解決方法一

> dir /a-d /b | findstr /v ".*\.bat$ .*\.js$ .*\.md$"

分析

dir命令

首先使用dir命令,找到當(dāng)前目錄下所有文件:

> dir /a-d /b 

命令行中使用> help dir可以查看該命令的參數(shù)詳解寂汇。
對上面這個命令病往,/a表示 顯示具有指定屬性(attribute)的文件,其后緊跟屬性:-表示“否”骄瓣,而d表示目錄文件(即文件夾)停巷。所以/a-d表示非文件夾的文件。
/b表示使用空格式(沒有標(biāo)題信息或摘要)榕栏。

C:\Users\Berlin\Desktop\demo>dir /a-d
 驅(qū)動器 C 中的卷沒有標(biāo)簽畔勤。
 卷的序列號是 F2B9-76C6

  C:\Users\Berlin\Desktop\demo 的目錄

2018/04/16  11:21               263 override.ts
2018/04/14  10:37           525,295 package-lock.json
2018/04/14  10:37             1,328 package.json
.....
              16 個文件        536,101 字節(jié)
               0 個目錄 83,691,253,760 可用字節(jié)

C:\Users\Berlin\Desktop\demo>dir /a-d /b
override.ts
package-lock.json
package.json
...

findstr命令

基本用法就是findstr <模板字符串> <待查找字符串>。這里模板字符串默認(rèn)是使用正則表達(dá)式的扒磁,可以使用通配符等庆揪,但是不能用圓括號的捕獲組。
例如為了匹配以.md渗磅、.js嚷硫、.bat為拓展名的文件,以JS風(fēng)格的正則表達(dá)式可能會寫成:.*\.(md|js|bat)$始鱼,注意第二個點字符要轉(zhuǎn)義仔掸,第一個點字符表通配符。

然而在這里將無法使用上述風(fēng)格的正則表達(dá)式医清,為了能夠匹配起暮,我們需要寫成

".*\.bat$ .*\.js$ .*\.md$"

即一個字符串,使用空格作為分隔,那么它就會匹配以.md负懦、.js筒捺、.bat為拓展名的文件。
假設(shè)有一個叫temp file.txtanother file.txt的文件纸厉,而只想匹配前者系吭,則表達(dá)式"^te.* e\.txt$"會把兩個文件都匹配到,而我們的初衷可能是希望匹配 以te開頭颗品、后跟任意多個字符肯尺、加一個空格、以e.txt 結(jié)尾的文件躯枢,anthore file.txt不符合這個規(guī)則则吟,不應(yīng)該被匹配到。
實際上锄蹂,因為這個表達(dá)式有空格氓仲,所以實際上會被拆分為兩個表達(dá)式:^te.*e\.txt$,顯然后者表達(dá)式可以把another file.txt匹配到得糜。
所以為了只匹配temp file.txt敬扛,需要寫成^te.*e\.txt,即點通配符也包含了空格朝抖。

另一種需求是舔哪,如果想把引號中的所有字符解釋為一個整體,那么可以使用/c:選項槽棍,例如,/c:".txt .bat"抬驴,這意味著檢索時將".txt .bat"看作是整體炼七,并且點字符就是點字符,不再具有正則表達(dá)式的通配符功效布持。也就是說豌拙,/c:選項后跟的字符串是一個普通字符串,并且是一個整體不分割题暖。

/c:選項可以使用多個按傅。例如,想檢索包含.js.md的文件名胧卤,可以使用命令dir /a-d /b | findstr /c:".js" /c:".md"完成唯绍。但問題在于,由于他解釋為普通字符串而不是正則表達(dá)式枝誊,所以類似package.json的文件也會被匹配到况芒,而我們本意是找到以它們結(jié)尾。此時$符也無法使用了叶撒。

最后绝骚,findstr命令有一個選項是/v耐版,它表示“只打印不包含匹配的行⊙雇簦”粪牲。而這個功能正是我們需要的。

綜上所述止剖,使用dirfindstr腺阳,利用管道,我們在命令行中完成了需求滴须。

解決方案二

這里使用bat文件來完成舌狗,并且使用另一種思路,即用FOR循環(huán)和IF語句完成扔水。重點是理解setlocal enableDelayedExpansion的用途痛侍。

@echo off
setlocal enableDelayedExpansion
for %%I in (*.*) do (
    set /a res=0
    if "%%~xI" == ".md" set /a res+=1 
    if "%%~xI" == ".js" set /a res+=1 
    if "%%I" == "%~nx0" set /a res+=1 
    
    if !res! equ 0 echo %%I
)

endlocal

分析

注意循環(huán)變量I的寫法:在命令行中是%I,而在批處理文件中是%%I魔市。并且在這里主届,循環(huán)變量只能是一個大寫或小寫字母組成。
使用FOR循環(huán)待德,在當(dāng)前目錄下所有的文件中(*.*)中遍歷君丁。每次循環(huán)時,設(shè)置一個變量res并初始化為0.

set/a選項表示將等號右邊的字符解釋為數(shù)學(xué)表達(dá)式将宪。如果沒有/a绘闷,那么就會把字符0賦給res。使用/a選項可以完成后續(xù)的+=等數(shù)學(xué)運(yùn)算较坛。具體使用help set查看幫助印蔗。

如果當(dāng)前文件的拓展名等于.md,則res自增1丑勤。

~x表示顯示拓展名华嘹,~n表示文件名,~nx表示文件名和拓展名法竞。例如耙厚,對于遍歷到package.json時,相關(guān)輸出如下:

  • echo %%I => package.json
  • echo %%~xI => .json
  • echo %%~nI => package
  • echo %%~nxI => package.json

%0表示批處理文件本身(即其值為字符串"foo.bat"

做字符串比較時岔霸,例如"%%~xI" == ".md"薛躬,要把變量I放到雙引號中,否則可能無法比較

當(dāng)三個if都完成后呆细,如果拓展名不是.md泛豪、.js.bat,那么res的值就為0诡曙,則輸出臀叙。

啟用延遲環(huán)境變量擴(kuò)展

語句

setlocal enableDelayedExpansion

將啟用延遲環(huán)境變量擴(kuò)展。為什么要這樣做呢价卤?
詳細(xì)講解見參考資料[1][3]劝萤。簡單來說,批處理程序是逐條執(zhí)行批處理腳本的慎璧,它會先讀入一條語句床嫌,然后對該語句里的變量作替換,然后開始解釋這個語句胸私。
例如:

set a=4 
set a=5&echo %a% 
echo %a%
  1. 第一條語句執(zhí)行完后厌处,局部變量a的值為4。
  2. 接下來岁疼,處理程序先讀入第二條語句(set a=5&echo %a%)阔涉,然后對變量進(jìn)行替換,此后就變成了set a=5&echo 4捷绒,替換完成后才開始執(zhí)行這個語句瑰排,因此最終結(jié)果是輸出4而不是5.
  3. 第二條執(zhí)行完后,變量a變成了5暖侨,所以第三句輸出5

這里的關(guān)鍵在于椭住,語句執(zhí)行之前,變量被替換為當(dāng)前的值字逗。因此在本條語句執(zhí)行過程中京郑,該變量的變化是不會被檢測到的(它會在之后的語句中生效,例如例子中的第三句)

為了能使得局部變量的變化能被檢測到葫掉,那么在執(zhí)行之前就不能對其進(jìn)行值替換傻挂,也就是延遲賦值。這就是為什么要使用setlocal enableDelayedExpansion的原因:

setlocal enableDelayedExpansion
set a=4 
set a=5&echo !a!
echo %a%

上面的批處理運(yùn)行結(jié)果是輸出兩個5. 我們注意到第三句中的變量a使用感嘆號進(jìn)行取值而不是百分號挖息。

由于啟動了變量延遲,所以批處理能夠感知到動態(tài)變化兽肤,即不是先給該行變量賦值套腹,而是在運(yùn)行過程中給變量賦值,因此此時a的值就是5了

啟用延遲賦值后资铡,應(yīng)該在適當(dāng)?shù)胤绞褂?code>endlocal命令進(jìn)行關(guān)閉电禀。

沒有使用endlocal語句停用延遲環(huán)境變量擴(kuò)展功能,則MS-DOS解釋器會在程序的末尾自動調(diào)用endlocal命令重置MS-DOS環(huán)境默認(rèn)值笤休。

回到本方案代碼中去尖飞,在最后一句IF判斷中,使用了

if !res! equ 0 echo %%I

res使用了延遲賦值(兩個感嘆號)。因此在之前的IF中如果res發(fā)生了變化政基,則此處也會被檢測到并使用贞铣。

作為一個反例,我們來看看下面的代碼:

@ECHO OFF
set res=Bonjour
for %%I in (A B C) do (
    set /a res=99
    echo %res%
)

在FOR循環(huán)開始之前對局部變量res賦值為Bonjour(因為沒有使用/a沮明,故認(rèn)為是字符串)辕坝。在FOR循環(huán)中,每一輪都將res賦值為數(shù)值99荐健,我們可能認(rèn)為會連續(xù)輸出三次99酱畅,但事實是,連續(xù)輸出三次字符串“Bonjour”:

C:\Users\Berlin\Desktop\demo>set_local.bat
Bonjour
Bonjour
Bonjour

這是因為江场,F(xiàn)OR循環(huán)的循環(huán)體使用括號括起來的纺酸,因此整個循環(huán)體語句實際上是一條語句!址否!餐蔬。因此在執(zhí)行FOR循環(huán)之前res已經(jīng)是“Bonjour”了,在執(zhí)行FOR的時候在张,自然會做變量替換用含,因此它等價于下面的程序

@ECHO OFF
set res=Bonjour
for %%I in (A B C) do (
    set /a res=99
    echo Bonjour
)

解決辦法:

@ECHO OFF
setlocal enableDelayedExpansion
set res=Bonjour
for %%I in (A B C) do (
    set /a res=99
    echo !res!
)
endlocal

輸出:

C:\Users\Berlin\Desktop\demo>set_local.bat
99
99
99

【要點】

  1. 使用setlocal enableDelayedExpansion開啟延遲賦值
  2. 局部變量使用!foo!進(jìn)行引用,而不是%foo%

參考資料

  1. bat中if語句的用法
  2. 請問批處理中符號%nx0和!num!%%xm表示什么意思呢帮匾?
  3. 批處理中setlocal enabledelayedexpansion的作用詳細(xì)整理
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末啄骇,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子瘟斜,更是在濱河造成了極大的恐慌缸夹,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件螺句,死亡現(xiàn)場離奇詭異虽惭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蛇尚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進(jìn)店門芽唇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人取劫,你說我怎么就攤上這事匆笤。” “怎么了谱邪?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵炮捧,是天一觀的道長。 經(jīng)常有香客問我惦银,道長咆课,這世上最難降的妖魔是什么末誓? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮书蚪,結(jié)果婚禮上喇澡,老公的妹妹穿的比我還像新娘。我一直安慰自己善炫,他們只是感情好撩幽,可當(dāng)我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著箩艺,像睡著了一般窜醉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上艺谆,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天榨惰,我揣著相機(jī)與錄音,去河邊找鬼静汤。 笑死琅催,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的虫给。 我是一名探鬼主播藤抡,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼抹估!你這毒婦竟也來了缠黍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤药蜻,失蹤者是張志新(化名)和其女友劉穎瓷式,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體语泽,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡贸典,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了踱卵。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片廊驼。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖惋砂,靈堂內(nèi)的尸體忽然破棺而出妒挎,到底是詐尸還是另有隱情,我是刑警寧澤班利,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站榨呆,受9級特大地震影響罗标,放射性物質(zhì)發(fā)生泄漏庸队。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一闯割、第九天 我趴在偏房一處隱蔽的房頂上張望彻消。 院中可真熱鬧,春花似錦宙拉、人聲如沸宾尚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽煌贴。三九已至,卻和暖如春锥忿,著一層夾襖步出監(jiān)牢的瞬間牛郑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工敬鬓, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留淹朋,地道東北人。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓钉答,卻偏偏與公主長得像础芍,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子数尿,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,585評論 2 359