起因
今天同学有一个批量改文件夹名字的需求,因为非计算机专业的同学电脑肯定不会有 python
或者 java
的环境了,那么 windows
自带的批处理编程一定是最好的选择了,新建一个 .txt
,改后缀命为 .bat
,双击就可以运行了。
利用批处理的一些命令,可以做很多事情,之前在知乎回答过一个问题,一行代码可以做什么。
里边提到了锁屏和 windows
计划任务的结合。
rundll32.exe user32.dll,LockWorkStation
定时关机,1800
秒后关机。
shutdown -s -t 1800
问题
他想把很多个文件夹的名字从 abcd1233-afdasfs
改成 1233 abcd1233-afdasfs
,所有文件夹的格式都是 4
个字母,4
个数字,然后一个-
,最后再跟一些字符。需要做的就是把4
个数字添加到文件夹名字的最前面,并且跟一个空格。
尝试一
我也是第一次写批处理的程序,但是不慌,编程嘛,重要的是算法,语言的语法查一查就可以了。所以需要解决下边几个问题。
- 定义变量
set name=XXX
注意的是,默认赋值就是赋值字符串,而且也不用加双引号
如果想赋值数字,需要再 set
后边添加命令参数 /a
。
set /a num=1
- 取出变量的值,百分号包裹变量名
%name%
- 输出变量的值
echo %name%
for
循环遍历所有文件夹名,所有变量都保存在了%%i
变量里,至于为啥加了两百分号,不要问,问的话,我也不知道 2333,就是规定而已。此外加了/d
命令参数,表示遍历文件夹
for /d %%i in (*) do (
- 因为我们要取到文件夹名字中的数字,所以要进行切割
set newname=%name:~4,4%
语法就是,字符串变量加冒号加~
,然后两个数字的含义分别是字符串开始的位置以及字符的个数,开始位置从零开始计数
- 更改文件夹名字
ren oldname newname
- 还有一个一定会用的,注释代码
两种, ::
加语句,或者 rem
加语句,推荐rem
吧,因为::
我遇到了不知道什么原因的错误。
知道了上边的一切,就可以写出代码了,但写完之后发现个问题,我们用 %name%
并不能得到变量的值,查了查,原来在 for
循环中要用 !name!
。并且开头加上setlocal enabledelayedexpansion
。
rem ehco 设置为 off,不然的话运行会显示每条语句
@echo off
rem 防止中文乱码的
chcp 65001
setlocal enabledelayedexpansion
for /d %%i in (*) do (
set name=%%i
echo !name!
rem 字符串合并,四个数字加上空格再加上之前的名字,不用双引号
set newname=!name:~4,4! !name!
rem 因为 newname 中有空格,所以要加双引号
ren !name! "!newname!"
)
echo 处理完成
pause
假如我们有下边的文件夹
然后把上边的代码复制保存为 .bat
,执行
很完美,达到预期。
问题升级
写完代码以后和同学确认了一下需求,出现了一个问题,有的文件夹名字是 (啊)abcd1233-afdasfs
、(啊啊)abcd1233-afdasfs
这样的形式,也就是说数字开始的位置不一定是 4
了。怎么办呢?
尝试二
我们只要知道 -
的下标,往前数 4
个数字就可以了,没有找到什么直接的方法,找到一种利用 goto
的方案。
goto
语法就是先用 :label
定义一个位置,然后 goto label
就可以实现循环了。
所以我们的想法就是遍历文件夹的名字的字符串,得到 -
的位置。
@echo off
chcp 65001
setlocal enabledelayedexpansion
for /d %%i in (*) do (
set name=%%i
echo !name!
set str=%%i
set /a num=0
:next
if not !str!=="" (
set /a num+=1
if "!str:~0,1!"=="-" goto last
set str=!str:~1!
goto next
)
set /a num=0
:last
echo 字符-在字符串"!name!"中的首次出现位置为!num!
set newname=!name:~4,4! !name!
)
echo 处理完成
pause
理想是美满的,现实是残酷的,本以为解决了,然后运行测试了一下。
比如我们有下边样子的文件夹
然后把上边的代码保存成 .bat
执行会发现结果是下边的样子
-
的位置找对了,但是…为什么只找了一次,我们的for
循环怎么没用了 。
几经试探,搜索。发现微软的批处理命令不知道基于什么考虑,如果我们在 for
循环中用了 goto
,那么 for
循环就会自动结束。没办法,我们得换思路了。
最终尝试
网上找了找,找到一种截取某一个字符前的字符串的方法。
for /f "delims=-" %%n in ('echo %%i') do (
)
这里的 %%n
就会保存 -
前边的字符串了。然后我们保存倒数四个的字符串就可以了。而倒数其实也提供了方法。
截取通过倒数方式指定开始位置的整个字符串:%key:~-2%,表示截取从倒数第 2 个字符开始的整个字符串
正数倒数方式相结合:%key:~2,-2%,表示截取从下标 2 开始到倒数第 2 个之间的字符串
所以我们最后的代码就是下边的了,注意用等号赋值的时候可能习惯左右加空格,这里就不要加了,会出错。
@echo off
chcp 65001
setlocal enabledelayedexpansion
for /d %%i in (*) do (
echo %%i
rem set newname=%%i
for /f "delims=-" %%n in ('echo %%i') do (
set name=%%n
set newname=!name:~-4! %%i
)
ren "%%i" "!newname!"
)
echo 处理完成
pause
执行前的文件夹
执行上边的代码
执行后的文件夹
完美!
结束
大功告成了。只能说批处理的命令坑太多了,非常不习惯,和现代编程语言太多的不同了。唯一的好处就是不用搭环境,写个文本文件直接运行。但对于这些文件处理,推荐学一下 python
,就会体会到优雅了。