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%Windowssystem32下,因此它就相当于
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:ackup %%i in (1) do echo %%i --枚举dackup目录
    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: estabc.txt这个文件的名字为xyz.txt的话,如果当前路径位于d: est,那么,命令可以写成:ren abc.txt xyz.txt、ren d: estabc.txt xyz.txt,

但是,绝对不能写成ren d: estabc.txt d: estabc.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 

原文地址:https://www.cnblogs.com/bkylee/p/4893390.html