WindowsBatch与LinuxShell比较[batch常见错误]
一 变量延迟
为了更好的说明问题,我们先引入一个例子。
例1:
@echo off
set a=4
set a=5&echo %a%
pause
结果:4
解说:为什么是4而不是5呢?在echo之前明明已经把变量a的值改成5了?
让我们先了解一下批处理运行命令的机制:
批处理读取命令时是按行读取的,在处理之前要完成必要的预处理工作,这其中就包括对该行命令中的变量赋值。我们现在分析一下例1,批处理在运行到这句“set a=5&echo %a%”之前,先把这一句整句读取并做了预处理——对变量a赋了值,那么%a%当然就是4了!(没有为什么,批处理就是这样做的。)
而为了能够感知环境变量的动态变化,批处理设计了变量延迟。简单来说,在读取了一条完整的语句之后,不立即对该行的变量赋值,而会在某个单条语句执行之前再进行赋值,也就是说“延迟”了对变量的赋值。
那么如何开启变量延迟呢?变量延迟又需要注意什么呢?举个例子说明一下:
例2:
@echo off
setlocal enabledelayedexpansion
set a=4
set a=5&echo !a!
pause
结果:5
解说:由于启动了变量延迟,得到了正确答案。变量延迟的启动语句是“setlocal enabledelayedexpansion”,并且变量要用一对叹号“!!”括起来(注意要用英文的叹号),否则就没有变量延迟的效果。
分析一下例2,首先“setlocal enabledelayedexpansion”开启变量延迟,然后“set a=4”先给变量a赋值为4,“set a=5&echo !a!”这句是给变量a赋值为5并输出(由于启动了变量延迟,所以批处理能够感知到动态变化,即不是先给该行变量赋值,而是在运行过程中给变量赋值,因此此时a的值就是5了)。
再举一个例子巩固一下。
例3:
@echo off
setlocal enabledelayedexpansion
for /l %%i in (1,1,5) do (
set a=%%i
echo !a!
)
pause
结果:
1
2
3
4
5
二 if中的判断
不多说了,直接看这两段代码。
1、
@echo off
set a=5
set b=40
if %a% gtr %b% echo 5比40大!
pause
rem 运行结果是“按任意键继续…”
2、
@echo off
set a=5
set b=40
if "%a%" gtr "%b%" echo 5比40大!
pause
rem 运行结果是“5比40大!”
比较一下这两段代码,其差别就在引号“""”上。即当变量没被引号括起来的时候进行的是数字的比较;而被引号括起来就是进行字符串的比较(比较字符串时当然5比40大)。
三 if中注意空变量
@echo off
set hero=
if %hero% equ 1 (echo 正确!) else (echo 错误!)
pause
错了吗?
答:错了。此时变量hero的值为空,即if语句相当于“if equ 1 (echo 正确!) else (echo 错误!)”,equ前面没有东西,当然不对。
解决方法:加上引号,即“if "%hero%" equ "1" (echo 正确!) else (echo 错误!)”
四 errorlevel使用的错误。
@echo off
find "hero" 1.txt
if errorlevel 0 echo 找到了!&goto next
if errorlevel 1 echo 没找到!&goto next
:next
pause
错了吗?
答:错了!这样写无论1.txt中有没有字符串“hero”都会显示“找到了!”。
原因是“if errorlevel 0 echo 找到了!&goto next”这句相当于“if %errorlevel% gtr 0 echo 找到了!&goto next”。因此错误码大的一定要写在前面。
五重定向的问题
如果你要向一个文件中写入数字1,也许你会这样写:
@echo off
echo 1>hero.txt
pause
但这样写是错误的,因为这里的数字1有其它用途--代表了
标准输出流。
一般情况下不要用个位数直接接重定向符号。
那么如何解决这个问题呢?我们可以换另外一种格式:
@echo off
>hero.txt echo 1
双引号在单个使用的时候非常容易犯错误。“"”后面的字符串在第二个“"”出现之前都会被当成一般字符!
@echo off
if hero==hero (echo hero is me")
pause
这段代码运行不成功,原因是引号后面的括号“)”被当作是一般字符了。
@echo off
echo hero is me"&pause
pause
这段代码也出乎了我们的意料,原因是引号后面的&被当作是一般字符了。
批处理中双引号的优先级比较高,但低于百分号,也就是说双引号中的百分号不会被识别为普通字符。
为了更好的说明问题,我们先引入一个例子。
例1:
@echo off
set a=4
set a=5&echo %a%
pause
结果:4
解说:为什么是4而不是5呢?在echo之前明明已经把变量a的值改成5了?
让我们先了解一下批处理运行命令的机制:
批处理读取命令时是按行读取的,在处理之前要完成必要的预处理工作,这其中就包括对该行命令中的变量赋值。我们现在分析一下例1,批处理在运行到这句“set a=5&echo %a%”之前,先把这一句整句读取并做了预处理——对变量a赋了值,那么%a%当然就是4了!(没有为什么,批处理就是这样做的。)
而为了能够感知环境变量的动态变化,批处理设计了变量延迟。简单来说,在读取了一条完整的语句之后,不立即对该行的变量赋值,而会在某个单条语句执行之前再进行赋值,也就是说“延迟”了对变量的赋值。
那么如何开启变量延迟呢?变量延迟又需要注意什么呢?举个例子说明一下:
例2:
@echo off
setlocal enabledelayedexpansion
set a=4
set a=5&echo !a!
pause
结果:5
解说:由于启动了变量延迟,得到了正确答案。变量延迟的启动语句是“setlocal enabledelayedexpansion”,并且变量要用一对叹号“!!”括起来(注意要用英文的叹号),否则就没有变量延迟的效果。
分析一下例2,首先“setlocal enabledelayedexpansion”开启变量延迟,然后“set a=4”先给变量a赋值为4,“set a=5&echo !a!”这句是给变量a赋值为5并输出(由于启动了变量延迟,所以批处理能够感知到动态变化,即不是先给该行变量赋值,而是在运行过程中给变量赋值,因此此时a的值就是5了)。
再举一个例子巩固一下。
例3:
@echo off
setlocal enabledelayedexpansion
for /l %%i in (1,1,5) do (
set a=%%i
echo !a!
)
pause
结果:
1
2
3
4
5
二 if中的判断
不多说了,直接看这两段代码。
1、
@echo off
set a=5
set b=40
if %a% gtr %b% echo 5比40大!
pause
rem 运行结果是“按任意键继续…”
2、
@echo off
set a=5
set b=40
if "%a%" gtr "%b%" echo 5比40大!
pause
rem 运行结果是“5比40大!”
比较一下这两段代码,其差别就在引号“""”上。即当变量没被引号括起来的时候进行的是数字的比较;而被引号括起来就是进行字符串的比较(比较字符串时当然5比40大)。
三 if中注意空变量
@echo off
set hero=
if %hero% equ 1 (echo 正确!) else (echo 错误!)
pause
错了吗?
答:错了。此时变量hero的值为空,即if语句相当于“if equ 1 (echo 正确!) else (echo 错误!)”,equ前面没有东西,当然不对。
解决方法:加上引号,即“if "%hero%" equ "1" (echo 正确!) else (echo 错误!)”
四 errorlevel使用的错误。
@echo off
find "hero" 1.txt
if errorlevel 0 echo 找到了!&goto next
if errorlevel 1 echo 没找到!&goto next
:next
pause
错了吗?
答:错了!这样写无论1.txt中有没有字符串“hero”都会显示“找到了!”。
原因是“if errorlevel 0 echo 找到了!&goto next”这句相当于“if %errorlevel% gtr 0 echo 找到了!&goto next”。因此错误码大的一定要写在前面。
五重定向的问题
如果你要向一个文件中写入数字1,也许你会这样写:
@echo off
echo 1>hero.txt
pause
但这样写是错误的,因为这里的数字1有其它用途--代表了
标准输出流。
一般情况下不要用个位数直接接重定向符号。
那么如何解决这个问题呢?我们可以换另外一种格式:
@echo off
>hero.txt echo 1
pause
六 罪恶的双引号
双引号在单个使用的时候非常容易犯错误。“"”后面的字符串在第二个“"”出现之前都会被当成一般字符!
@echo off
if hero==hero (echo hero is me")
pause
这段代码运行不成功,原因是引号后面的括号“)”被当作是一般字符了。
@echo off
echo hero is me"&pause
pause
这段代码也出乎了我们的意料,原因是引号后面的&被当作是一般字符了。
批处理中双引号的优先级比较高,但低于百分号,也就是说双引号中的百分号不会被识别为普通字符。
七 罪恶的空格,空格不能随便乱用,定义变量的时候不要再等号前后加空格,还有很多的情况都需注意。
八 续行符
想必大家都知道符号“^”是作为去掉后续符号特殊意义的前导符。“^”还可以作为续行符,就是指出下一行是所在行的延续。
举个简单的例子:
@echo off
echo 英雄^
是^
好^
男人
pause
九 再看重定向
我们知道一个创建快捷方式的方法:
echo [InternetShortcut] >>a.url
echo URL=c:\windows\system32\notepad.exe >>a.url
echo IconIndex=20 >>a.url
echo IconFile=C:\windows\system32\shell32.dll >>a.url
是不是觉得代码中每一句都要加上“>>a.url”很繁琐。其实有一个简单的方法:
(
echo [InternetShortcut]
echo URL=c:\windows\system32\notepad.exe
echo IconIndex=20
echo IconFile=C:\windows\system32\shell32.dll
)>a.url
完!