bat programming is easy and powerful
用linux的角度来思考windows,习惯了linux的shell后,
再来看windows的bat编程,就简单多了,简直就是理所当然
实际上windows的cmd命令行和linux的shell命令行感觉基本上差不多了
比如都有重定向 > , >>, 和管道 |
ipconfig /a >> ip.txt
dir c: |more
tree c: | find "call" (tree显示的结果像图行,其实是字符串)
要习惯于看windows cmd的命令帮助,command /? ,里面的解释已经很详细很直白了
这就像linux下的man “command”
-----------------------
当你熟练掌握了linux中的sed, awk, cut,grep等这些行编辑命令后,
自然就能灵活地、得心应手地使用cmd中for if 等等命令的使用
for [/options] %var in (set) do command [command-parameters]
FOR /F "eol=; tokens=2,3* delims=, " %i in (...)...
cmd支持命令简写:
rd=rmdir,md=mkdir,ren=rename,
type:显示文本文件到标准输出,print是真的用打印机打印输出
prompt:修改命令提示符,对应linux bash中是设置环境变量ps的值
用prompt命令,可以将cmd设置成类似bash的样子:>prompt [$d $t $p] #
move:移动文件,linux是mv命令
xcopy:复制文件和目录树
find:在文件内按行搜索字符串,linux中类似的命令是grep
-------------------------------------
cmd把命令后面的[options]叫开关switches,bash叫选项options
cmd的开关通常不能合并在一起书写,而bash的选项可以合在一起
find /V /N "somestring" filename
netstat -tunl |grep "telnet"
windows的系统命令一般放在%SystemRoot%\Windows\system32下,因此它就相当于
linux的/sbin, /bin, /usr/bin, /usr/sbin
命令shell中,每个命令执行完毕后,都会返回一个返回码,成功为0,失败为1.
echo %errorlevel%: linux中用$! 表示
------------------------------
变量延迟:
在由&,&&,||,()组成的符合句中,变量的值取该语句之前的值,在该符合句中可以给变量赋值,
但变量的新值,要在该语句的下一句才会生效,即:变量的值会“延迟“起作用
利用变量延迟,可以交换两个变量的值而不用中间变量;
要避免“变量延迟”的问题,使变量的新值在该复合句中即时生效,应该:
setlocal enabledelayedexpansion
..... echo !delayed_var!
变量延迟的机制:
因为bat执行命令的机制是:首先将一条命令读入内存,在执行该命令之前,先做一些预处理工作,包括:替换循环变量、
如:
test]# for %i in (f1 f2) do ren %i %i.mp3 //执行时会首先将%i分别替换成f1, f2,然后才执行
test]# ren f1 f1.mp3
test]# ren f2 f2.mp3
给该语句中的 “变量 %var%”赋值,这时候,因为这条语句还没有执行,所以当然是用该语句之前的值给
变量赋值, 然后才执行这条语句。
为了让语句中的变量,能够及时感知环境的变化,使用“变量延迟”技术, 让语句在执行之前,再次对其中的变量赋值,...
事实上,在bat编程中,变量延迟也用得相当“普遍”,很平常的:凡是在for循环语句的结构体中,要改变“非迭代变量”的变量 的值,如
累加,字符串连续串接等,都要使用变量延迟
setlocal ... 注意和endlocal配合使用
bat中可以使用通配符?,*
?: 表示0个或1个字符,*表示0个或任意个,注意?可以是没有,可以是0,并不一定要有一个字符
如: dir /b ????? : 将匹配1到5个字符的所有文件(夹)
--------------------
在web,c,c++编程中,对于比较大的项目,往往会通过代码分割,写成多个文件
同样的,如果要用bat实现比较复杂的任务(以后习惯用bat脚本来实现处理 windows下的任务)时,
就要编写多个脚本.bat文件,这时相互之间的调用就要使用call命令...(但要注意call语句不同于函数调用,因为
它不会阻塞原程序的执行)
bat编程中,没有while...do的结构,要使用循环,是用 if...goto “lable"来实现的
同样,也没有switch...case的结构,也是用 if...goto “lable"来实现的
-----------------------------
if语句的逻辑与和逻辑或?
在bat编程中,不能用&& ||来表示逻辑与/或(有&&, ||,但它们是用来连接语句的), 要表示多个条件的 “逻辑与” 应该使用if语句的嵌套:
if "%str1" == "%str2" (
if "%str3"== "%str4%" (
rem do something....
)
)
choice命令?
有的windows中没有choice命令,因为choice不是内部命令,它是一个外部命令,choice.exe/choice.com
但是也没有必要,一定用choice命令,因为用set /p, if, goto 命令完全可以代替choice命令...
字符串的连接?
bat脚本中字符串连接直接挨着写就好了,不用什么+.
-------------------------------------
bat编程中的pushd和popd的有什么作用?
当需要在多个目录中反复切换时,使用pushd和popd是最好的方法,比cd命令更方便更直观
linux中的pushd和popd,dirs命令:
pushd parameter_dir ==== “current_dir" pushd + cd parameter_dir
cd - 其中的- : = $OLDPWD
pushd; pushd +2; popd; popd +1; 栈总是从上到下, 从0, 1,2 开始的,所以切换时总是整数+加号
dirs -c: 清除栈中所有的目录
popd命令总是不能清除当前正在使用的目录!
windows: pushd %~dp0 ; pushd “%~dp0test" ... popd: 即: 切换到某个目录,执行完某些操作后,再返回到原来的目录
--------------------------------------------
--------------------------------------------
for循环:
: for变量的扩展是“全局”的,即:跟使不使用/d, /r , /f等完全无关
:for的4种形式:包括:不使用扩展的纯的裸的、使用/d的、使用/l的、使用/f的,
每种格式都是有严格规定的,只能使用每种情况下规定的格式和许可的内容: 如:
for %%i in (*.mp3)/(*) do echo %%i : 这个是可以的,可以使用通篇符,而且主要是使用通配符
for /f %%i in (*.mp3)/(*) do echo %%i : 这个就是不可以的,因为在for /f 的三种格式里都没有使用通配符
默认的for循环(不带任何扩展参数的时候) 只处理file-set,(将把set当作一个或一组文件,是文件)不处理目录
file-set的表示方法只有两种?!: 一种是使用通配符*,?, 另一种是直接输入文件名列表集合,用空格分割
file-set中并不能执行命令,不像linux中使用后引号表示命令执行结果,
(for命令的set集合中,要想使用 “子命令“的执行结果,需要使用 /f 扩展参数!!)
所以,即使你单或 双引号,for也只是认为那是几个文件的集合:
如: for %%i in ( 'dir /b mp3') do (ren %%i %%i.mp3) 将会认为dir, /b, mp3是三个文件,
将它们重命名,自然执行 ren /b /b.mp3就会出错了
for /d: 对这个参数,以前一直有个误解,认为只匹配文件夹,对文件不起作用。实际上,这种想法是错误的:
1. 有这个/d与没有/d,对文件的解析完全是没有影响到,文件该解析该匹配的,照样匹配,照样执行命令就好象没有这个/d一样
2. 只有当后面括号中使用通配符*, *.*, (????.????足够多?)的时候,才匹配目录,而不匹配文件,也就是说,只有/d跟* 在一起的时候,才起作用
for /r : 参数/r, 搜索指定路径及所有子目录中与set相符合的所有文件,注意是指定路径及所有子目录。
1、set中的文件名如果含有通配符(?或*),则列举/R参数指定的目录及其下面的所用子目录中与set相符合的所有文件,无相符文件的目录则不列举。
2、如果set中为具体文件名,不含通配符,则枚举该目录树(即列举该目录及其下面的所有子目录)(并在后面加上具体的文件名),而不管set中的指定文件是否存在。
例:for /r c:\ %%i in (*.exe) do echo %%i --把C盘根目录,和每个目录的子目录下面全部的EXE文件都列出来了
for /r c:\ %%i in (boot.ini) do echo %%i --枚举了c盘所有目录
for /r d:\backup %%i in (1) do echo %%i --枚举d\backup目录
for /r c:\ %%i in (boot.ini) do if exist %%i echo %%i --很好的搜索命令,列举boot.ini存在的目录
for /f:
前面的for都只是处理文件和目录,把它们作为一个整体进行处理,不涉及文件里面的内容,
但如果要对文件里面的内容进行处理的话,就要用/f 扩展,但并不表示for /f不能用来处理文件
而且只有这个时候,才能使用单引号,双引号,后引号等,才能使用 执行命令结果
for /f: 以前有一种误解:因为for /f要将file-set中的每个文件的内容要读入内存进行处理,所以就以为
for /f就不能用来处理文件,只能用来处理文件的内容。其实for /f同样可以用来处理文件,如:
文件名的输出,文件的重命名,文件的复制删除等等,只不过这时我不需要、我可以忽略:文件内容的分割处理等结果
我不处理那些%%j, %%k, 等等的“隐藏变量”, 我只处理%%i 这个基本的文件循环变量本身就可以了(这时的%%i
代替的是文件本身,跟使用tokens的%i代表的是不同的!) 当然,当我需要对文件里面的内容进行解析、分割,
并输出解析结果时,我可以要tokens, delims等等... 而且for /f格式也说得很清楚: for /f [“options”] %var in (3种格式) do ... 那些分割解析的只是:[options]!
for %i in (set) do格式只能使用通配符或一个一个的列举方式的fileset,
如果这两种方式都不适合,要用dir命令来列举的时候,就要使用: for /f %i in ('dir /b /a-d /s') do echo %i....
:最神奇的是,当tokens和%%cycle_var连用的时候, 在for /f "tokens=1,2,3,4" %i in ("this is a test") do echo %i %J %k %L中,隐含变量(%i后面的变量)是要分顺序的!只能是j,k,l...不能是其他字母或大写的字母,否则就无效!
如:上面的语句,只输出: this %J a %L 而且这里的%i代表的不再是文件名本身,而是分割后的tokens...
---------------------------------------
:dir 要只显示文件,就要把属性directroy过滤掉: dir /a-d, 要修改文件的属性,需要使用attrib命令...
:有时候,用*或者?也可以代替for循环?如对文件夹下的所有文件进行重新命名: 可以不使用for循环,直接用:
rename * *.mp3 // 增加扩展名
rename *.* *.mp3 // 全部扩展名更改为mp3
rename ????.txt ????.mp3
rename ?????????????????????????.txt ?????????????????????????.mp3( ?问号越多, 可以命名的文件越多?)
这里要注意的是: 问号?是不能代替扩展名的.xxx, 在上面的命令中,使用rename ???? ????.mp3将是错误的???
: 通配符虽然可以代表任意字符, 但是 在一次循环中,前后代表的字符/文件/含义在当前这次循环/操作中,
“通常”是一致的,"后面的可以替代前面的,但前面的*不一定就能代替后面的内容“,如:
ren *.mp3 * // 这个命令本意是要去掉文件的扩展名,但这样实际上文件是没有改变和影响到
:关于rename的“古怪"规定: 新的文件名不能带路径,即使跟原来的路径相同也不行:
(网上的说法:
旧文件可以使用绝对路径,也可以使用相对路径,但是,新文件名不能使用任何路径,只能是新的文件名,即使这个路径就是当前目录。
例如:需要修改 d:\test\abc.txt这个文件的名字为xyz.txt的话,如果当前路径位于d:\test,那么,命令可以写成:ren abc.txt xyz.txt、ren d:\test\abc.txt xyz.txt,
但是,绝对不能写成ren d:\test\abc.txt d:\test\abc.txt这样的格式。
之所以会有这个古怪的规定,可能是一旦把路径写成另外的目录,ren就具备了“移动文件+重命名文件”的功能 了,这和它的定位不相符(内部的代码编写就跟move重复了)。
)
:要遍历整个目录,dir命令的/S选项,以及for命令的/R选项都有遍历功能
: 很重要的一点是: 要对bat中的语句进行排错,一是在命令行一句一句执行,二是开启echo on 可以看到
(交互式的)每一句执行的结果,清楚地看到是哪条语句出错
:注意两种变量的不同表示方法:循环变量用%%i表示,而普通变量用 %var%表示
--------------------------------------
bat举例:
rem @echo off
cls
setlocal enabledelayedexpansion
echo author: bkylee
echo date: %date%,%time%
echo functionality: batch to delete mp3 files extention
echo scenario: the command "ren *.mp3 *" can not bring about the result above
echo.
pushd %~dp0%
rem if not exist "mp3\" (
rem echo "error 1: no mp3 folder"
rem goto end
rem )
echo --------------------------------------------------
echo start to rename...
set warnings = ""
for %%i in (*) do (
if "%%~xi" == ".mp3" (
ren %%i %%~ni
rem ----: 在下面的else前后都必须有一个空格,使else与括号) else ( 相隔离,否则就会出错!
) else (
set warnings = !warnings!"%%i is not mp3 file"
)
)
echo --------------------------------------------------
dir /b
rem recover the enviornment before
echo.
echo %warnings%
echo.
endlocal
popd
:end
echo "press any key to exit ..." & pause >nul
资料来源于网络,其中,参考这篇文章,写得比较好,比较细:http://biancheng.dnbcw.info/python/341315.html