[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖

[转贴] 批处理内部命令对错误返回码errorlevel的影响

转自:http://tieba.baidu.com/f?kz=579067365
原因:所在地无法上帖吧,在这里留个备份
提示:本文内容仅原文转载,未做任何测试!

注:本文不考虑chdir,erase,mkdir,rename,rmdir,因为它们与cd,del,md,ren,rd同义,因此有37个内部命令,内部命令加 /? 不改变 errorlevel  

管道符与重定向,如 |,&,||,&&,>,>>,1>,2> 等不改变 errorlevel  
以下20个命令如果执行成功,不改变 errorlevel  
assoc break call cls echo
endlocal for ftype goto if
path pause popd prompt rd
rem set shift start title

call 是否改变 errorlevel 由调用的内容决定
color 在 Windows 2000 中不改变 errorlevel
exit /b 不加退出码不改变 errorlevel
for 命令 in 后的括号内如果是命令,则这个命令不改变errorlevel  
rd 命令在文件夹非空或不存在时,也不改变 errorlevel  
set 删除变量,返回1, set 可以直接修改 errorlevel ,这样会使 errorlevel 成为一般变量,以后不能用来记录命令返回值,但可用 IF [NOT] ERRORLEVEL number command 语句判断 errorlevel
start 命令如果加 /w 或 /wait 选项,可以改变 errorlevel
以上结果仅凭个人测试

以上的我也没有测试,(仅对SET试了一下),LZ保存的扩展名应该是BAT,不知扩展名为CMD时能否逃过这些
枫中残雪:风停了,我的心却在动,让我心中的寒意走向远方

TOP

今天碰到头疼的事了...请问 tree 是否不存在大于 0 的返回值呢?如果有,又是什么时候?另外,如何通过最简洁高效的命令来判断一个文件是否不存在子文件夹呢?我原本打算在代码中用类似下面的思路:
  1. tree "%cd%" >nul 2>nul||echo 不存在子文件夹
复制代码
但是调试了几遍总是不对,最后发现 tree 好像碰到无子目录的情况也不会返回大于 0 的 errorlevel

TOP

猜测是tree函数(假设tree命令对应tree函数)不会返回非零值。微软的程序有的也很矬。
关键是看不到cmd源码啊。

TOP

4# powerbat


Thanks,看来貌似真的没有指望了?唉,微软真不给力啊

TOP

echo命令由于简单,怎么使他失败呢?——所以echo不改变errorlevel的返回值(只成功不失败,自然返回值也就一个),那20个都是这个原因吗?
  1. 以下20个命令如果执行成功,不改变 errorlevel  
  2. assoc break call cls echo
  3. endlocal for ftype goto if
  4. path pause popd prompt rd
  5. rem set shift start title
复制代码

TOP

6# Hello123World
首先有些命令会有失败的情况
比如set

其次执行成功不设置errorlevel
不等同执行成功设置errorlevel为0
比如执行前errorlevel为3
在执行echo成功后errorlevel为3
而不是0
天的白色影子

TOP

7# qzwqzw


这是否说明部分内部命令不存在返回值呢?比如 echo 和 if、for

TOP

楼主看这里:
|| 会改变退出码的实例
话说以前好像也碰到过 || 改变退出码的情况,不知道是不是记错了

TOP

本帖最后由 qzwqzw 于 2014-12-1 22:49 编辑

很早的请求
最近终于有了些分析结果

最终结论
||确实与& && 不同
如果其左侧语句执行返回非0的值
||会将这个返回值写入errorlevel


9楼链接中的语句
(for /f %%a in ('dir /ad /b test') do break)
在当前目录不存在test时
它的返回值为1(实际是cmd /c dir /ad /b test的返回值)
只是for语句单独执行时这个返回值并不写入errorlevel
而当与||联用时
||会将其返回值写入errorlevel

顺便解答一下7楼的问题
这里出现了一点认识上的混淆
几乎所有命令都会有返回值
但是并非所有返回值都会写入errorlevel
比如echo命令执行后会返回零值
但这个零值并不写入errorlevel(推测是为了向下兼容DOS)
而&& || 是依据返回值作为判断条件的
这也是为什么echo没有写errorlevel
却仍然可以放在&&与||的左侧参与逻辑运算

另外的测试分析发现
errorlevel也不全是函数的返回值
比如执行不存在的命令时errorlevel是9009
但这条语句的返回值实际上是1
因此下面语句的输出结果是1而非9009
$xxx || echo !errorlevel!
而如果使用使用以下命令则可以看出||也可以写入非1的errorlevel
replace $xxx $yyy || echo !errorlevel!

参考链接:
[讨论]对批处理中errorlevel的几点猜测
http://www.bathome.net/redirect. ... 18&fromuid=3023
1

评分人数

    • CrLf: 大概如此吧技术 + 1
天的白色影子

TOP

本帖最后由 CrLf 于 2014-11-25 02:59 编辑

回复 10# qzwqzw


挑个刺:
它的返回值为1(实际是cmd /c dir /ad /b test的返回值)

cmd /c dir /ad /b test 返回值为 0
真正改变内部“返回值”的是无取值的 for /f,再由 || 把它表现到 errorlevel 变量中:
  1. (for /f %%a in ("") do echo 这里不可能显示)||echo !errorlevel!
复制代码

TOP

本帖最后由 qzwqzw 于 2014-11-25 11:34 编辑

不好意思
编辑过程中
误操作点了删除
导致回复帖也无法显示了

我这里测试cmd /c dir /ad /b test会将errorlevel置1
不知道你是如何测试的

以下语句for /f有取值(此处有误)errorlevel也会被置1
  1. (for /f %%f in ('$xxx') do echo+%%f)||echo+!errorlevel!
复制代码
这说明for的返回值设置是很复杂的
要论证到底是何缘由需要更仔细的分析测试
天的白色影子

TOP

本帖最后由 CrLf 于 2014-11-25 04:03 编辑

回复 12# qzwqzw


其实是我看你的回复不见了之后自己删的,然后默默地修改了原帖...
dir 的返回值确实可能非 0,这我混乱了,但 11 楼的刺仍然部分成立
12 楼的 $xxx 输出是在 StdOut,不在 StdIn,所以 for /f 的输入仍然为空
致命一击:
  1. (for /f %%f in ('$xxx 2^>^&1') do echo+%%f)||echo+!errorlevel!
复制代码
  1. (for /f %f in ('"dir 一个不存在的文件 2>&1 >nul"') do echo+%f)||echo+[!errorlevel!]
复制代码
所以我认为 for /f 的返回值与其中命令的返回值无关,而仅与 for /f 有无输入以及 for 本身的参数错误有关,详见下方测试结果
-------------------------------------------------------------------------------------
无输入时等于 1:
  1. (for /f %%f in (' ') do break)||echo+!errorlevel!
复制代码
  1. (for /f %%f in ("") do break)||echo+!errorlevel!
复制代码
  1. (for /f "skip=99999999" %%f in (a.txt) do break)||echo+!errorlevel!
复制代码
参数错误也等于 1:
  1. (for /f "错误的参数" %%f in ("test") do break)||echo+!errorlevel!
复制代码
也有等于 2 的时候:
  1. (for /f %%f in (一个不存在的文件) do break)||echo+!errorlevel!
复制代码
有趣的是,好像有且仅有一种情况是等于 3 的:
  1. (for /f %%f in (^") do break)||echo+!errorlevel!
复制代码
1

评分人数

    • qzwqzw: 辛苦了PB + 6 技术 + 1

TOP

本帖最后由 qzwqzw 于 2014-11-25 11:29 编辑

回复 13# CrLf
真正改变内部“返回值”的是无取值的 for /f,再由 || 把它表现到 errorlevel 变量中:

我现在也同意你的这个观点
昨晚我也是混乱了
看来确实不能熬夜了
还是你精力充沛啊

经过分析
for /f的返回值分为三种情况

第一种是for选项及遍历集解析错误的返回值
这个基本都是1

第二种是for的遍历集set创建时的返回值
因为这个set可以是文件名
所以主要是使用CreateFile()打开文件时返回的
常见的返回值包括
ERROR_SUCCESS
0 (0x0)
The operation completed successfully.

ERROR_INVALID_FUNCTION
1 (0x1)
Incorrect function.

ERROR_FILE_NOT_FOUND
2 (0x2)
The system cannot find the file specified.

ERROR_PATH_NOT_FOUND
3 (0x3)
The system cannot find the path specified.

所以13楼最后代码中的^"被判断成了一个无效的路径
使用C:\$xxx\$yyy可以得到同样的返回值

另外ERROR_SUCCESS是不会被for/f返回的
因为当打开文件成功后for/f仍要继续解析set遍历集及do执行语句

还有些不常见的返回值
打开C:\System Volume Information时的ERROR_ACCESS_DENIED(5)
打开C:\WINDOWS\system32\config\sam时的ERROR_SHARING_VIOLATION(32)
具体错误代码参考以下链接
http://technet.microsoft.com/zh-cn/magazine/ms681382(en-us,VS.85).aspx

第三种是do后执行语句后的返回值
这个经过测试只取最后一句的返回值

12 楼的 $xxx 输出是在 StdOut,不在 StdIn,所以 for /f 的输入仍然为空

一个小问题
$xxx 输出是在 stderr,而不是stdout,所以 for /f 的输入仍然为空
1

评分人数

    • CrLf: 感谢指正技术 + 1
天的白色影子

TOP

返回列表