Bat语法总结笔记

1   推荐链接

2   检测并获取管理员权限

2.1   方式一:使用cacls工具检测权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@chcp 65001>nul
@echo off & title 自动激活系统 & color F0
cd /d "%~dp0"

@REM 查询当前终端窗口的列数大小
for /f "tokens=2 delims=: " %%c in ('mode con ^| findstr Columns') do set cols=%%c

@REM 局部环境设置打印的符号和数量,并传递到全局环境draw_line变量中
setlocal enabledelayedexpansion
set "char=="
set /a "count=%cols%"
set "line="
for /l %%i in (1,1,%count%) do set "line=!line!%char%"
endlocal & set "draw_line=%line%"

cls
echo %draw_line%
echo.

@REM 检测权限:根据系统版本尝试访问系统文件路径
if "%PROCESSOR_ARCHITECTURE%" equ "amd64" (
"%SYSTEMROOT%\SysWOW64\cacls.exe" "%SYSTEMROOT%\SysWOW64\config\system" >nul 2>&1
) else (
"%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system" >nul 2>&1
)

@REM 根据权限情况跳转标签
if "%errorlevel%" neq "0" (
echo 当前检测无管理员权限。正在请求管理员权限...
echo.
goto uac_prompt
) else (
goto got_admin
)

@REM 获取管理员权限
:uac_prompt
powershell -Command "Start-Process -Verb RunAs -FilePath '%0' -ArgumentList 'am_admin'"
exit /b

@REM "已获取管理员权限,存储当前目录并切换到脚本程序目录"
:got_admin
pushd "%cd%"
cd /d "%~dp0"

2.2   方式二:使用fsutil dirty query检测权限

1
2
3
4
5
6
7
8
@echo off
echo.
set "params=%*"
cd /d "%~dp0" && ( if exist "%temp%\getadmin.vbs" del "%temp%\getadmin.vbs" ) && %systemdrive% 1>nul 2>nul || ( echo Set UAC = CreateObject^("Shell.Application"^) : UAC.ShellExecute "cmd.exe", "/c cd ""%~sdp0"" && %~s0 %params%", "", "runas", 1 >> "%temp%\getadmin.vbs" && "%temp%\getadmin.vbs" && exit /B )
@echo off & title Install latest office & color 70
@chcp 65001>nul
echo.
cd /d %~dp0

2.3   方式三:提权到System权限

1
2
3
4
@REM 获取管理员权限
:uac_prompt
powershell -ep bypass "Install-Module -Name NtObjectManager;start-Win32ChildProcess cmd"
exit /b

3   提权并传递参数

1
2
3
4
5
6
7
8
9
10
11
12
13
@REM 提权之前先保存当前的命令行参数
@REM 保存命令行参数到txt文件
set commandline_argument=%*
echo %commandline_argument% > "__temp__argument.txt"

@REM 提权代码《检测并获取管理员权限》

@REM 重新读取参数
@REM 读取txt文件中的首个参数
for /f "tokens=1" %%c in (__temp__argument.txt) do (
set get_argument=%%c
goto activate
)

4   字符串的处理

4.1   字符串的截取

%variable:~n,m%

  • n:开始截取字符串的偏移量;如果为正数,则从左边开始;如果没有为负数,则从右边开始
  • m:要截取字符的个数。如果没有指定个数,则采用默认值,即变量数值的余数(余数指剩余个数,如:%variable:~-5% 当前偏移量为倒数第6,将剩下的字符全部截取)。如果两个数字 (偏移量和长度) 都是负数,使用的数字则是字符串长度加上指定的偏移量或长度(参见实例2)。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    set ifo=abcdefghijklmnopqrstuvwxyz0123456789
    echo 原字符串:
    echo %ifo%

    rem abcde
    echo 截取前5个字符:
    echo %ifo:~0,5%

    rem fghijklmnopqrstuvwxyz0123456789
    echo 截取第六个字符直到最后一个字符
    echo %ifo:~5%

    rem 56789
    echo 截取最后5个字符:
    echo %ifo:~-5%
    echo %ifo:~-5,5%

    rem abcdefghijklmnopqrstuvwxyz01234
    echo 截取第一个到倒数第6个字符:
    echo %ifo:~0,-5%

    rem defgh
    echo 从第4个字符开始,截取5个字符:
    echo %ifo:~3,5%

    rem wxyz0
    echo 从倒数第14个字符开始,截取5个字符:
    echo %ifo:~-14,5%

4.2   字符串的字符替换

%variable:str1=str2%

1
2
@REM 替换str中的xxx为yyy
set str2=%str:xxx=yyy%

4.3   去掉变量字符串中的空格

1
2
3
4
5
6
7
8
9
10
11
12
set abc=                          aaabbbccc
:delleft
if "%abc:~0,1%"==" " set abc=%abc:~1%&&goto delleft
echo 去掉左边空格:%abc%
set abc=aaabbbccc
:delright
if "%abc:~-1%"==" " set abc=%abc:~0,-1%&&goto delright
echo 去掉右边空格:%abc%
@REM 去除所有空格,使用的方式就是字符替换
set abc= aaa bbb ccc
set "abc=%abc: =%"
echo 去掉所有空格:%abc%

5   读取内容

for /f”常用来解析文本,读取字符串。

  • delims参数负责切分字符串,指定分隔符。内容会根据实际分隔符分隔开。
  • tokens负责提取字符串,指提取的列数。例如tokens=12-4,表示提取第1列,第2列至第4列的内容,有多少列就要有多少个输出变量,并且各变量中的字母存在先后顺序。第一个变量字母是自己定义的,推荐是使用%%a,这样第二个列就是用%%b来表示。

5.1   读取首行内容

1
for /f "usebackq delims=" %%a in ("!drive!\!config_file!") do set "image_name=%%a"

5.2   读取特定行数内容

1
for /f "tokens=* delims=" %%i in ('diskpart /s auto_partition_opts ^| findstr /n "^" ^| findstr "^9:"') do set disk_model_name=%%i

6   判断网络连通状态执行不同命令

1
ping www.baidu.com -n 3 | findstr "TTL" >nul && goto network_well || goto network_bad

7   将批处理运行结果显示并保存到文件

7.1   wtee方案

linux上有tee命令可以实现这个目的,wtee程序则是windows实现的tee版本。

7.2   临时文件中转方案

原理:使用call将想要输出结果输出到临时文件,之后进行:

  1. 先清屏,将临时文本内容显示,实现窗口显示的本次运行结果的功能。
  2. 将临时文本内容追加到日志文件用于保存。
  3. 删除临时文件。
    1
    2
    3
    4
    5
    6
    7
    8
    @echo off
    call a.bat > log2.txt
    cls
    for /f "delims=" %%i in (log2.txt) do (
    echo %%i
    )
    type log2.txt >> log.txt
    del log2.txt

8   数组

创建数组:set a[0]=1

定义值列表并遍历值列表:

1
2
3
4
5
@echo off
set list=1 2 3 4
(for %%a in (%list%) do (
echo %%a
))

遍历数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
@echo off
setlocal enabledelayedexpansion
set topic[0]=comments
set topic[1]=variables
set topic[2]=Arrays
set topic[3]=Decision making
set topic[4]=Time and date
set topic[5]=Operators

@REM for /L %%变量 in (起始值,每次增值,结束时的比较值) do 命令 ,in (0,1,5) 会生成一个序列 [0,1,2,3,4]
for /l %%n in (0,1,5) do (
echo !topic[%%n]!
)

计算数组长度:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@echo off
set Arr[0]=1
set Arr[1]=2
set Arr[2]=3
set Arr[3]=4
set "x=0"
:SymLoop

if defined Arr[%x%] (
call echo %%Arr[%x%]%%
set /a "x+=1"
GOTO :SymLoop
)
echo "The length of the array is" %x%

数组结构体:

1
2
3
4
5
6
7
8
9
10
11
@echo off
set obj[0].Name=Joe
set obj[0].ID=1
set obj[1].Name=Mark
set obj[1].ID=2
set obj[2].Name=Mohan
set obj[2].ID=3
FOR /L %%i IN (0 1 2) DO (
call echo Name = %%obj[%%i].Name%%
call echo Value = %%obj[%%i].ID%%
)

9   setlocal和endlocal的作用域

在setlocal和endlocal语句内的set变量,是属于局部变量。

如果用setlocal enabledelayedexpansion设置了延迟环境变量扩展,需要使用!variable!来引用变量。

10   生成特定命令的批处理帮助文件

1
2
3
4
5
6
@echo on
:z
@cls
@set /p "d=输入要BAT指令:"
@ %d% /? > %d%.txt
goto z

11   字符集引起的“is not recognized as an internal or external command”

知识背景:系统终端默认的字符集是ANSI,但ANSI并非某一种特定的字符编码,在不同的系统中,ANSI表示不同的编码。比如原生英文系统的ANSI编码是ASCII编码,原生中文系统的ANSI编码是GBK编码。(code page实际是由系统的「区域和语言」控制的)

问题现象:当bat程序脚本文件本身用默认的utf-8编码保存,并且使用命令@chcp 65001>nul切换代码页,仍然有概率执行是会导致echo命令在输出中文出现解析错误,提示“is not recognized as an internal or external command”,比如使用注释命令@rem 中文注释之后,再使用echo打印输出中文时就出现解析错乱。

问题原因:具体引起的原因未知,目前猜测是当脚本程序执行不少命令之后,环境变量可能会出现错乱,导致部分命令解析失败。

解决方法有两种:

  • @rem 中文注释命令之后重新执行@chcp 65001>nul即可确保后面的命令正确解析执行。
  • 将所有文件编码改为ANSI编码,比如简中系统,使用GBK编码。

12   在命令提示符中运行 PowerShell 命令

命令形式:powershell -command "xxx"

如果命令中有双引号,记得使用反斜杠转义双引号字符,避免最外层的双引号和里面的双引号一起被匹配解析。在PowerShell语法中,双引号 "" 视为文字字符串值的。因此需要确保符号被正确解析。

13   语法问题

13.1   ==equ的对比区别

  • ==是用来比较相同的,即比较字符串是否完全相同的。
  • EQU是比较运算符,含有运算的功能,换句话说,是可以在比较之前转换数字为对应的数值,然后再比较。

数值是指十进制的数字,在前缀加 (这里x忽略大小写)则表示十六进制数字,加 0 则表示八进制数字。因此,0x1218 相同,也与 022 相同,八进制表示法容易引起混淆。例如,0809 不是有效数字,因为 89 不是有效的八进制数字。

所以,在使用 == 比较时,0x1202218是不相等的,但在使用EQU比较时,他们是相等的,因为他们对应的数值都是18

14   参考文献

[1] How to request Administrator access inside a batch file[EB/OL]. https://stackoverflow.com/questions/1894967/how‑to‑request‑administrator‑access‑inside‑a‑batch‑file.
[2] How to write a batch file can run parameters as command with administrative privi‑leges?[EB/OL]. https://stackoverflow.com/questions/38364617/how‑to‑write‑a‑batch‑file‑can‑run‑parameters‑as‑command‑with‑administrative‑priv.
[3] 批处理通过 ping 判断网络[EB/OL]. https://www.52pojie.cn/thread-1021861-1-1.html.
[4] 批处理 ping命令判断是否可以连接到一个网站[EB/OL]. https://lanlan2017.github.io/blog/318f3e6b/.
[5] Windows10一句话从administrator权限提升到system权限[EB/OL]. https://blog.csdn.net/mochu7777777/article/details/106842901.
[6] 在命令提示符中运行 PowerShell 命令[EB/OL]. https://www.delftstack.com/zh/howto/powershell/running-powershell-commands-in-cmd/.
[7] [系统相关] [已解决]批处理if中,== 与equ 这2个,在什么情况下会有区别?[EB/OL]. http://www.bathome.net/thread-15170-1-1.html.
[8] cmd 字符串替换[EB/OL]. https://blog.csdn.net/scimence/article/details/52816878.
[9] Bat 去掉变量字符串中的空格[EB/OL]. https://blog.csdn.net/a71468293a/article/details/127747191.
[10] 字符串截取操作[EB/OL]. https://www.hxstrive.com/subject/windows_bat/122.htm.
[11] 在使用bat 批处理 时将运行结果显示并保存到文件中 echo[EB/OL]. https://www.cnblogs.com/qiyuexin/p/11701362.html.
[12] for /f命令之—Delims和Tokens用法&总结[EB/OL]. https://blog.csdn.net/kagurawill/article/details/114982328.
[13] 批处理(bat)中的数组问题[EB/OL]. https://blog.csdn.net/qq_34414530/article/details/117839838.
[14] BAT批处理文件 setlocal,endlocal命令详解[EB/OL]. https://blog.csdn.net/csqxy547/article/details/89856034.
[15] [数值计算] [已解决]批处理()嵌套的变量延时与endlocal作用域问题[EB/OL]. http://www.bathome.net/thread-32880-1-1.html.