Windows BAT批处理由于CRLF问题引发的奇怪错误

问题非常奇怪,基本的日志输出都会出错,代码如下:

1. 问题非常奇怪,基本的日志输出都会出错

  • 代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @echo off  
    setlocal enabledelayedexpansion

    :: 获取当前日期和时间,格式可能因系统区域设置而异
    set "currentTime=%date% %time%"

    :: 替换日期和时间格式中的特殊字符(如果需要)
    set "currentTime=!currentTime:/=-!"

    :: 输出当前时间加上消息内容到文件,如果文件不存在则创建
    echo [!currentTime!] 这是你的消息内容 >> output.log

    endlocal

    代码没有什么功能实现,只是简单的输出日志到文件

  • 运行结果:

1
2
3
4
5
6
7
$ ./speed-starter.bat
'替换日期和时间格式中的特殊字符(如果需要)' is not recognized as an internal or external command,
operable program or batch file.
'消息内容到文件,如果文件不存在则创建' is not recognized as an internal or external command,
operable program or batch file.
'me!]' is not recognized as an internal or external command,
operable program or batch file.

奇怪的错误,完全看不出问题的原因

2. 问题解决

对于这个问题简直是毫无头绪,检测语法完全没有问题。最后为了确认语法没问题,使用VSCode新建另外一个代码内容完全一样的Bat文件:start.bat

1
2
3
4
5
6
7
8
9
10
11
12
13
@echo off  
setlocal enabledelayedexpansion

:: 获取当前日期和时间,格式可能因系统区域设置而异
set "currentTime=%date% %time%"

:: 替换日期和时间格式中的特殊字符(如果需要)
set "currentTime=!currentTime:/=-!"

:: 输出当前时间加上消息内容到文件,如果文件不存在则创建
echo [!currentTime!] 这是你的消息内容 >> output.log

endlocal

运行发现没有任何错误,日志也输出到了 output.log
于是重新运行之前的 starter.bat 还是相同的错误!这就奇怪了!
为了验证两个文件的差别,用 VSCode 同时左右分屏打开,代码内容确实完全一样!还是毫无头绪。

最终不经意注意到了 VSCode 在切换两个文件时,右下角的状态栏由变化:

  • starter.bat -> LF
  • start.bat -> CRLF

CRLF 我以前有了解过,就是不同操作系统对文本文件的换行符号有所不同,Windows 下的换行是 \r\n,也叫回车换行,对应 CRLF
所以 starter.bat 只有 LF 没有 RC,脚本解释器把所有代码都认为在同一行执行,出错时当然的了!

所以解决办法就是把 LF 切换到 CRLF 模式。这得借助 VSCode 或者 IDEA 这样的高级编辑器,记事本估计不行。

3. CRLF 知识补课

CRLF是“Carriage Return Line Feed”的缩写,它表示回车换行,对应的ASCII码是“\r\n”。

关于CRLF,以及不同操作系统之间的差异,以下是一些相关知识:

    1. CR、LF与CRLF的含义与起源
    • CR(Carriage Return,回车):对应ASCII中的转义字符’\r’,其十进制ASCII代码是13,十六进制代码为0x0D。在机械打字机时代,CR的作用是将打字机上的滚动托架(Carriage)滚回到打印纸张的最左侧,但保持当前打字的垂直位置不变,即还是在同一行。
    • LF(Line Feed,换行):对应ASCII中的转义字符’\n’,其ASCII代码是10,十六进制为0x0A。LF的作用是将打印纸张上移一行位置,但保持当前打字的水平位置不变。
    • CRLF:即CR+LF,表示回车并换行,对应的ASCII码是“\r\n”。
    1. 不同操作系统中的换行符差异
    • Windows系统:采用CRLF(\r\n)表示回车换行。这意味着在Windows平台上,换行在文本文件中是使用两个字节(0d 0a)来表示的。

    • Unix/Linux系统:采用LF(\n)表示换行。在Unix/Linux系统中,每行的末尾只有newline(即“\n”),换行符由0a一个字节表示。

    • Mac系统:早期的Mac OS系统(如MacIntosh)采用CR(\r)表示回车作为换行;而现代的Mac OS X系统则与其他 Unix-like 系统一样,采用LF(\n)作为换行符。

    1. 换行符差异的影响:这种差异在文本编辑和跨平台软件开发中可能会导致问题。例如,当代码开发者在不同的系统上都编辑过同一份代码并来回传输时,或者当多人协作开发一个项目并使用版本控制系统(如git)进行源码管理时,就可能会出现换行符不统一的问题。这可能会导致代码在不同系统上的表现不一致,或者在版本控制系统中产生不必要的差异。