Windows 批处理脚本学习教程

Collected by Clove

第七章:常用实例 下

如果您已从头至尾完整地忍受过了前六章的基础,那么就让我们来做一些更疯狂的批处理吧……

7.1 坐在家里周游世界

没错,我想是认真的,对于任何您想去往地球上的地点,您所需要做的仅仅是提供一个目的地的经纬度坐标而已。当然,我是不会给您提供去往世界各地的签证和机票的,但是海量的卫星照片还是能让您过足了瘾的。此时您手头可能并没有足够的坐标资源,不过我这里"恰好"有些陈芝麻烂谷子,是当年在留园网上搜集的地理坐标,具体请参阅《关于Google Maps的趣点》。这里必须得先感谢卫星图片的提供者 Google Maps ,因为你知道,毕竟航拍卫星并不是人人都能买的起的。

本小节也是在我回顾《关于Google Maps的趣点》这篇文章开始的。因为当年在搜集这些该死的地理坐标时,偷了一些懒,结果有些是小数格式的,有些是度分秒格式的,查看起来并不方便。如果有 Google Earth 来转换它们之间的格式倒也不难。但我知道,此时您的迅雷网快电驴里已经排满了各种各样电影、游戏、动漫的下载任务,看来您并没有下载 Google Earth 的意思。没关系,下面这个批处理多少能让您更方便地浏览 Google Maps 上的卫星照片。

::::::坐在家里周游世界.bat::::::
@echo off
title 坐在家里周游世界
:: 设置标题
:Start
cls
:: 清屏
set choice=1
set /p choice=请选择经纬度格式(1. 小数格式 2. 度分秒格式 3. 退出):
:: 选择经纬度的格式 或 退出程序
if %choice%==3 goto :EOF
if %choice%==2 goto :DMSFormat

:DecimalFormat
:: 小数格式的经纬度处理
set latitude=39.906477
set longitude=116.391467
:: 初始化为伟大首都天安门的坐标
set /p latitude=纬度:
set /p longitude=经度:
:: 提示用户输入目标的经纬度 [注1]
goto :LaunchMap

:DMSFormat
:: 度分秒格式的经纬度处理
set "DMSlatitude=39 54 23 N"
set /a degree=39
set /a minute=54
set /a second=23
set NorthOrSouth=N
:: 初始化为美丽首都天安门度分秒格式的纬度
echo.
set /p DMSlatitude=纬度(格式 [度] [分] [秒] [N ^| S]):
:: 提示用户输入目标的纬度 [注2]
for /f "tokens=1,2,3,4,5" %%i in ("%DMSlatitude%") do (
set degree=%%i
set minute=%%j
set second=%%k
set NorthOrSouth=%%l
if "%%l"=="" echo 不正确的格式 & goto :DMSFormat
)
:: 分别获得纬度的度、分、秒,以及南半球或北半球
if %degree% lss 0 echo 纬度必须大于0度 或不正确的格式 & goto :DMSFormat
if %degree% gtr 90 echo 纬度必须小于90度 或不正确的格式 & goto :DMSFormat
if %minute% lss 0 echo 纬度必须大于0分 或不正确的格式 & goto :DMSFormat
if %minute% gtr 60 echo 纬度必须小于60分 或不正确的格式 & goto :DMSFormat
if %second% lss 0 echo 纬度必须大于0秒 或不正确的格式 & goto :DMSFormat
if %second% gtr 60 echo 纬度必须小于60秒 或不正确的格式 & goto :DMSFormat
:: 判断纬度的度、分、秒格式是否有效 [注3]
set /a degree*=3600
set /a minute*=60
set /a second=%degree%+%minute%+%second%
set /a latitude=%second%*2500/9
:: 将度分秒计算并转换为小数格式 [注4]
if %NorthOrSouth%==N goto :LatitudeLockDown
if %NorthOrSouth%==n goto :LatitudeLockDown
if %NorthOrSouth%==S set /a latitude=0-%latitude% & goto :LatitudeLockDown
if %NorthOrSouth%==s set /a latitude=0-%latitude% & goto :LatitudeLockDown
:: 判断南北半球格式是否有效
echo 南北半球标识不明确,请使用 N 或 S 来表示,不区分大小写。 & goto :DMSFormat

:LatitudeLockDown
set "DMSlongitude=116 23 29 E"
set /a degree=116
set /a minute=23
set /a second=29
set EastOrWest=E
:: 初始化为可爱首都天安门度分秒格式的经度
echo.
set /p DMSlongitude=经度(格式 [度] [分] [秒] [E ^| W]):
:: 提示用户输入目标的经度
for /f "tokens=1,2,3,4" %%i in ("%DMSlongitude%") do (
set degree=%%i
set minute=%%j
set second=%%k
set EastOrWest=%%l
if "%%l"=="" echo 不正确的格式 & goto :LatitudeLockdown
)
:: 分别获得经度的度、分、秒,以及东半球或西半球
if %degree% lss 0 echo 经度必须大于0度 或不正确的格式 & goto :LatitudeLockDown
if %degree% gtr 180 echo 经度必须小于180度 或不正确的格式 & goto :LatitudeLockDown
if %minute% lss 0 echo 经度必须大于0分 或不正确的格式 & goto :LatitudeLockDown
if %minute% gtr 60 echo 经度必须小于60分 或不正确的格式 & goto :LatitudeLockDown
if %second% lss 0 echo 经度必须大于0秒 或不正确的格式 & goto :LatitudeLockDown
if %second% gtr 60 echo 经度必须小于60秒 或不正确的格式 & goto :LatitudeLockDown
:: 判断经度的度、分、秒格式是否有效
set /a degree*=3600
set /a minute*=60
set /a second=%degree%+%minute%+%second%
set /a longitude=%second%*2500/9
:: 将度分秒计算并转换为小数格式
if %EastOrWest%==E goto :LongitudeLockDown
if %EastOrWest%==e goto :LongitudeLockDown
if %EastOrWest%==W set /a longitude=0-%longitude% & goto :LongitudeLockDown
if %EastOrWest%==w set /a longitude=0-%longitude% & goto :LongitudeLockDown
:: 判断东西半球格式是否有效
echo 东西半球标识不明确,请使用 E 或 W 来表示,不区分大小写。 & goto :LatitudeLockDown

:LongitudeLockDown
set latitude=%latitude:~0,-6%.%latitude:~-6%
set longitude=%longitude:~0,-6%.%longitude:~-6%
:: 整理纬度和经度

:LaunchMap
echo.
set /a zoom=16
set /p zoom=放缩度(0~18 默认值:%zoom%):
:: 提示用户输入照片的放缩值
echo.
echo 正在打开 纬度:%latitude% 经度:%longitude% 的卫星照片...
start "正在打开Google Maps..." "http://maps.google.com/?t=k&z=%zoom%&ll=%latitude%,%longitude%"
:: 将放缩值和经纬度值作为 Google Maps 链接参数,打开相应的照片 [注5]

pause
goto :Start
::::::::::::::::::::::::::::::::

注1. 纬度或经度尽量能精确到小数点后3位数以上,因为越精确的坐标越能准确地帮您找到目标的位置

注2. 度分秒格式的纬度(或经度)您大概并不陌生,如果高中时代的您没有选择在地理课上逃课的话。例如 39°54' 23.32" N 就表示北纬39度54分23.32秒。另外,正在上高中的朋友在政治课上可以适当地翘课,个人研究表明,政治这玩意学多了不利于青少年大脑的发育。

注3. 纬度不能超过90度,而经度不能超过180度,分和秒的范围是0~59,这些常识是绝不会难到您的。

注4. 经纬度从度分秒格式转换为小数格式只需要:(度*60*60+分*60+秒)/60/60 即可,北纬39度54分23.32秒转换为小数即39.906477度。事实上,此处latitude的值是实际纬度的一百万倍,因为DOS命令中并不支持浮点型(实数)的变量,不用担心,在后面会有小数点向前移动6位的处理。同时,希望您也没有在数学课上翘课的习惯。

注5. 该链接正指向此时经纬度和放缩值的卫星地图,其中具体参数的含义可以参考《关于Google Maps的趣点》文中的解释。

好吧,您可能已经迫不及待地想试试这东西了。那么在运行该批处理后,首先您会得到选择两种经纬度类型的提示。选1的话,只需要分别输入小数格式的纬度和精度,以及放缩值即可(如果您还不确定放缩值是啥东西的话,可以置空直接使用默认值)。
如果在程序的一开始选择了2,也就是度分秒格式的经纬度。比如我们的目的地是:北纬15°17' 55" ,东经19°25' 47" ,您可以依次在纬度和经度里输入 15 17 55 n 和 19 25 47 e ,然后在放缩值里填上 22 。这样您就能看到北非中部一个小村庄里的几位村民,以及他们的奶牛和骆驼。


坐在家里周游世界.bat 的运行界面


坐在家里周游世界.bat 的运行结果

事实上,上面的照片是 Google Maps 中罕见的几张高清卫星照片之一,并不是每张照片的放缩值都能达到23滴,因为你也知道,即使是能买得起航拍卫星的家伙,也是没有足够的资金和精力来把地球的每一个角落都拍得一清二楚的。

========================================朴实的分割线========================================

7.2 进程分析者

写了一篇很简单却又很占篇幅的玩意,被我称之为"进程分析者"。此批处理的构思很轻松,只是简单地使用 tasklist 列出所有的进程,再显示为容易理解的文字说明而已。这与任务管理器中的进程相比,除了易于识别以外,还能帮您鉴别出那些喜欢"偷梁换柱"的隐患进程。比如用肉眼去观察 winhlep.exe 或 winhe1p.exe 的时候很容易忽视它们,而使用"进程分析者"却不会。

:::::::::进程分析者.bat:::::::::
@echo off
setlocal enabledelayedexpansion

title 进程分析者
set SPACE=
set /a NumOfTotal=0
set /a NumOfSafe=0
set /a NumOfNasty=0
set /a NumOfUnknown=0
set IconOfSafe=√
set IconOfNasty=×
set IconOfUnknown=?

:::::::: 以下定义为可信任的进程,可自定义更多的扩充 ::::::::
:: 1. 系统进程
set alg.exe=%IconOfSafe%处理Windows网络连接共享和网络连接防火墙[系统进程]
set csrss.exe=%IconOfSafe%管理Windows图形相关任务[系统进程]
set explorer.exe=%IconOfSafe%用于显示系统桌面的图标,任务栏等[系统进程]
set lsass.exe=%IconOfSafe%用于本地安全和登陆策略[系统进程]
set services.exe=%IconOfSafe%管理启动和停止服务[系统进程]
set smss.exe=%IconOfSafe%用于对话管理子系统调用和系统对话操作[系统进程]
set spoolsv.exe=%IconOfSafe%用于将打印机任务发送到本地打印机[系统进程]
set svchost.exe=%IconOfSafe%用于执行动态链接库DLL文件[系统进程]
set System=%IconOfSafe%[系统进程]
set taskmgr.exe=%IconOfSafe%任务管理器,用于显示系统正在运行的进程[系统进程]
set winlogon.exe=%IconOfSafe%用于处理系统的登陆和登陆过程[系统进程]
set winmgmt.exe=%IconOfSafe%用于系统管理员创建WIndows管理脚本[系统进程]
:: 2. 基本进程
set cmd.exe=%IconOfSafe%Windows系统的命令行程序
set msimn.exe=%IconOfSafe%OutlookExpress相关程序
set mspaint.exe=%IconOfSafe%画图板
set notepad.exe=%IconOfSafe%记事本
set wab.exe=%IconOfSafe%通讯薄,用于储存地址、联系人和Email
set ctfmon.exe=%IconOfSafe%提供语音识别、手写识别等
set conime.exe=%IconOfSafe%输入法编辑器相关程序
set SOUNDMAN.EXE=%IconOfSafe%Realtek声卡相关程序
set tasklist.exe=%IconOfSafe%这是本批处理的核心所在-_-b
set wmiprvse.exe=%IconOfSafe%用于通过WinMgmt.exe程序处理WMI操作
:: 3. 工作进程
set EXCEL.EXE=%IconOfSafe%Excel
set WINWORD.EXE=%IconOfSafe%Word
set XDICT.EXE=%IconOfSafe%金山词霸
set sqlservr.exe=%IconOfSafe%用于SQL基础服务
set wmplayer.exe=%IconOfSafe%Windows Media Player
set Mplayerc.exe=%IconOfSafe%暴风影音
set WinRAR.exe=%IconOfSafe%WinRAR
:: 4. 防护进程
set 360tray.exe=%IconOfSafe%360安全卫士实时监控程序
set AntiArp.exe=%IconOfSafe%ARP防火墙
set CCenter.exe=%IconOfSafe%瑞星信息中心
set RavMonD.exe=%IconOfSafe%瑞星监控程序
set rfwsrv.exe=%IconOfSafe%瑞星个人防火墙相关程序
set RavStub.exe=%IconOfSafe%瑞星杀毒软件相关程序
set RfwMain.exe=%IconOfSafe%瑞星防火墙主程序
set RavTask.exe=%IconOfSafe%瑞星定时杀毒程序
set RavMon.exe=%IconOfSafe%瑞星监控程序
:: 5. 网络进程
set iexplore.exe=%IconOfSafe%IE浏览器
set Maxthon.exe=%IconOfSafe%傲游浏览器
set BaiduHi.exe=%IconOfSafe%百度Hi
set msmsgs.exe=%IconOfSafe%MSN网络聊天工具
set QQ.exe=%IconOfSafe%腾迅QQ
set TXPlatform.exe=%IconOfSafe%腾迅平台
set Thunder5.exe=%IconOfSafe%迅雷下载
set Skype.exe=%IconOfSafe%Skype语音聊天
set Contentfilter.exe=%IconOfSafe%Skype的相关程序
set skypePM.exe=%IconOfSafe%Skype语音聊天

:::::::: 以下定义为已知的危险进程,可自定义更多的扩充 ::::::::
set a.exe=%IconOfNasty%蠕虫
set av.exe=%IconOfNasty%蠕虫
set blss.exe=%IconOfNasty%木马/拨号器
set cmd32.exe=%IconOfNasty%病毒
set crss.exe=%IconOfNasty%蠕虫
set csrse.exe=%IconOfNasty%病毒/木马
set Desktop.exe=%IconOfNasty%木马/病毒/间谍
set directs.exe=%IconOfNasty%蠕虫
set dllhlp.exe=%IconOfNasty%木马
set dllreg.exe=%IconOfNasty%病毒
set explore.exe=%IconOfNasty%灰鸽子
set explored.exe=%IconOfNasty%蠕虫
set optimize.exe=%IconOfNasty%拨号器/广告
set pcsvc.exe=%IconOfNasty%间谍
set rundll16.exe=%IconOfNasty%木马
set run32dll.exe=%IconOfNasty%间谍
set scvhost.exe=%IconOfNasty%木马/广告
set svchosts.exe=%IconOfNasty%木马
set system32.exe=%IconOfNasty%木马
set updater.exe=%IconOfNasty%蠕虫
set web.exe=%IconOfNasty%病毒/木马
set win32.exe=%IconOfNasty%病毒
set windows.exe=%IconOfNasty%蠕虫
set winlogin.exe=%IconOfNasty%病毒/木马
set winstart.exe=%IconOfNasty%间谍/广告
set wintsk32.exe=%IconOfNasty%病毒
set winupdate.exe=%IconOfNasty%病毒
set winxp.exe=%IconOfNasty%病毒
set winhlep.exe=%IconOfNasty%病毒

:::::::: 以下定义为未知的进程 ::::::::
set UnknownTask=%IconOfUnknown%未识别的进程

:: 程序开始
echo 进程名称 分析结果
echo.

for /f "tokens=1" %%i in ('tasklist /NH') do (
set TaskName=%%i%SPACE%
set TaskName=!TaskName:~0,20!
if defined %%i (
echo !TaskName! !%%i!
if "!%%i:~0,1!"=="%IconOfNasty%" (
set /a NumOfNasty+=1
)
) else (
echo !TaskName! %UnknownTask%
set /a NumOfUnknown+=1
)
set /a NumOfTotal+=1
)
echo ________________________________________________________________
echo.
echo 共 %NumOfTotal% 个进程。
if %NumOfNasty% gtr 0 (
echo %NumOfNasty% 个存在安全隐患的进程!
)
if %NumOfUnknown% gtr 0 (
echo %NumOfUnknown% 个未知进程。
)
pause>nul
::::::::::::::::::::::::::::::::

结果:


图7-3 进程分析者.bat 的运行结果

分析完毕!

========================================朴实的分割线========================================

7.3 公元2738年11月28日是星期几[编写中]

公元2738和11月28日,是个好日子。我打赌您不知道这天究竟是星期几而且您也并不想知道。

:::公元2738-11-28是星期几.bat:::[β版]
@echo off
setlocal enabledelayedexpansion

:Initialization
:: 初始化
set TargetDate=%date:~0,4%%date:~5,2%%date:~8,2%
set /a Year = 0 & rem 年 范围: 1~9999
set /a Mon = 0 & rem 月 范围: 1~12
set /a Day = 0 & rem 日 范围: 1~28 29 30 31
set /a IsLeapYear = 0 & rem 是否闰年 范围: 0~1

:PromptEntering
:: 提示输入日期
set /p TargetDate=请输入日期(格式: YYYYMMDD 例如: %TargetDate%):

:: 转换为有效的年月日[注1]
if %TargetDate:~0,3% == 000 (
set /a Year = %TargetDate:~3,1%
) else (
if %TargetDate:~0,2% == 00 (
set /a Year = %TargetDate:~2,2%
) else (
if %TargetDate:~0,1% == 0 (
set /a Year = %TargetDate:~1,3%
) else (
set /a Year = %TargetDate:~0,4%
)
)
)
if %TargetDate:~4,1% == 0 (
set /a Mon = %TargetDate:~5,1%
) else (
set /a Mon = %TargetDate:~4,2%
)
if %TargetDate:~6,1% == 0 (
set /a Day = %TargetDate:~7,1%
) else (
set /a Day = %TargetDate:~6,2%
)

:CheckLeapYear
:: 检测是否是闰年[注2]
set /a DivisionBy4 = %Year%%%4
set /a DivisionBy100 = %Year%%%100
set /a DivisionBy400 = %Year%%%400

if %DivisionBy4% == 0 set /a IsLeapYear = 1 & rem 能被4整除的是闰年
if %DivisionBy100% == 0 set /a IsLeapYear = 0 & rem 能被100整除的非闰年
if %DivisionBy400% == 0 set /a IsLeapYear = 1 & rem 能被400整除的是闰年

set /a Month1 = 31
set /a Month2 = 28 + %IsLeapYear%
set /a Month3 = 31
set /a Month4 = 30
set /a Month5 = 31
set /a Month6 = 30
set /a Month7 = 31
set /a Month8 = 31
set /a Month9 = 30
set /a Month10= 31
set /a Month11= 30
set /a Month12= 31

:: 检测范围是否有效[注3]
if %Year% lss 1 echo 年份应该大于0 & goto :Initialization
if %Year% gtr 9999 echo 年份不能超过9999 & goto :Initialization
if %Mon% lss 1 echo 月份不能小于1 & goto :Initialization
if %Mon% gtr 12 echo 月份不能大于12 & goto :Initialization
if %Day% lss 1 echo 日数不能小于1 & goto :Initialization
if %Day% gtr 31 echo 日数不能大于31 & goto :Initialization
if %IsLeapYear% == 0 (
if %Mon%%Day% == 229 echo %Year%年的2月没有29日 & goto :Initialization
)
if %Day% gtr !Month%Mon%! echo %Mon%月没有%Day%日 & goto :Initialization

:: 开始计算天数
set /a Year -= 1 & rem 该年未结束,应减一年
set /a Day = %Day% + Year * 365 & rem 一年365天
set /a Day = %Day% + Year/4 & rem 每4年多一闰
set /a Day = %Day% - Year/100 & rem 可每100年少一闰
set /a Day = %Day% + Year/400 & rem 但每400年还多一闰

set /a Mon -= 1 & rem 该月未结束,应减一月
for /l %%i in (1,1,%Mon%) do (
set /a Day = !Day! + !Month%%i! & rem 累计前几月的天数
)

:: 计算星期
set /a Week = %Day%%%7 & rem 除7得余数
set WeekChars=日一二三四五六
set Week=!WeekChars:~%Week%,1! & rem 换成汉字

:: 显示结果
echo.
echo %TargetDate:~0,4%年%TargetDate:~4,2%月%TargetDate:~6,2%日,是公元第%Day%天,星期%Week%
echo ___________________________________________
echo. & pause>nul & goto :Initialization
::::::::::::::::::::::::::::::::

注1 正如第三章关于 set 为变量赋予数值型的值所说的,如果数值是以 0 开头的,那么该数值便是八进制的。这并不是我们所期望,因此对变量的首位数非零的检测是必要的。

注2 闰年每隔四年来一次,这是众所周知的,但它并不完全正确。因为一年约为365.24219天,因此闰年将遵循于“四年一闰,百年不闰,四百年再闰”的规则。

注3 每个月的天数并不固定,但它们都在28~31这个范围内。

公元2738年11月28日是星期一,跟 Garfield 一样,我也讨厌星期一。

========================================朴实的分割线========================================

7.4 163邮箱登录器之不显示密码篇

还记得我们在3.4节中所提到过的163信箱登录的问题吗,那是一个不错的小批处理。不过那个批处理存在着一个小小的缺憾:在输入密码时,密码会暴露无遗地直接显示在屏幕上,这对于注重个人隐私的您来说,显然是无法接受的。

:::::::163邮箱登录器.bat::::::::
@echo off
title 163邮箱登录器
mode con cols=80 lines=25
::设置窗口的尺寸

set /p username=用户名:
cls

chcp 437>nul
graftabl 936>nul
:: 转换代码页编号为 936

echo 用户名: %username%
set /p =密 码: <nul

echo hP1X500P[PZBBBfh#b##fXf-V@`$fPf]f3/f1/5++u5x>in.com
:: 创建一个神奇的二进制可执行文件
for /f "tokens=*" %%i in ('in.com') do set password=%%i
:: 在那个神奇的文件被执行完返回结果之前,不显示任何东西
del in.com

setlocal EnableDelayedExpansion
for /l %%i in (0,1,255) do (
if not "!password:~%%i,1!"=="" (
set /p =*<nul
) else (
endlocal
goto :LoginMail
)
)
endlocal
:: 计算密码长度,然后显示相应个数的星号

:LoginMail

start "正在登录邮箱" "https://reg.163.com/logins.jsp?username=%username%&password=%password%&url=http://fm163.163.com/coremail/fcg/ntesdoor2"

echo.
echo 正在登录邮箱...
ping -n 2 127.0>nul
:: 等待一小段时间
::::::::::::::::::::::::::::::::

备注:本小节尚存在问题待解决(输入密码时使用退格会被当作字符记录下来,不输入任何东西则将显示Invalid keyboard code specified)……

Collected by Clove--Mail to Me

Windows 批处理脚本学习教程