问题分析:该任务主要有三个问题要解决1)如何存放指定的文件夹,2)如何遍历文件夹下的所有文件;3)如何将遍历的文件输出到文件中。下面就逐一来解决这些问题。
问题解决:
1. 如何存放指定的文件夹
在Shell中主要通过set 来存放环境变量。比如:Set var=xxxx。如果要删除该变量,则使用Set var=。在shell脚本中并没有类似数组这样的变量来帮助你存放数据。所以只能考虑将几个文件名放到一个string中,然后通过字符串识别来分别读出这些变量。示例如下:
SET DirList=C:/A C:/B C:/D
FOR /D %%i IN (%DirList%) DO ECHO %%i
(如直接在命令行输入,%%i应写为%i)
利用FOR语句就可以分别读出C:/A,C:/B和C:/D。此处也算是对FOR /D命令的一种灵活运用了。当然除此之外,也可以将变量存放于某文件,然后再循环读出,此处就不再详细说明了。
上面的语句还有些不完善的地方,那就是不支持文件名中出现空格的情况。比如C:/D E, 这种情况,命令只能识别出空格前的部分,造成文件名错误。避免这种情况的方法就是用引号把文件名包起来,如下所示:
SET DirList=”C:/A” “C:/B” “C:/D E”
2. 如何遍历文件夹中的所有文件呢?
解决了多个文件夹名存放的问题,现在需要遍历文件夹了。幸运的是shell为我们提供了一个命令可以方便的遍历完文件夹中的文件。命令如下:
FOR /R [[drive:]path] %variable IN (set) DO command [command-parameters]
于是很自然的想到了如下的语句:
SET
DirList
=
”C:
A” “C:
B” “C:
D E”
FOR
/
D %%m IN
(
%DirList%
)
DO
(
FOR
/
R %%m %%i IN
(
*
)
DO
ECHO
%%i
)
但不幸的是,这个脚本运行时出错。主要问题就是在第二个FOR语句中并不支持%%m, 在path位置上出现%%m让FOR有点不知所措。于是我们想到是否可以先把%%m赋值给一个变量,然后再将变量用于FOR语句。修改后结果如下:
SET
DirList
=
”C:
A” “C:
B” “C:
D E”
FOR
/
D %%m IN
(
%DirList%
)
DO
(
SET
DIR
=
%%m
FOR
/
R %
DIR
% %%i IN
(
*
)
DO
ECHO
%%i
)
很不幸,程序是可以运行,但结果为空。因为对于一个完整的复合语句,比如此处的循环嵌套,其中用到的变量已经在运行前全部替换成当前的环境变量的值,运行时环境变量的改变并不会影响程序中正在使用的那个值。由于在FOR运行前DIR还为定义,第二个FOR循环中自然找不到要遍历的目录了,所以就什么结果都不会有了。
J
这下可该如何是好呢?感觉有点走头无路了。只好再看看帮助了,发现shell提供了setlocal enabledelayedexpansion语句可以让程序打开延迟扩展特性,使用临时变量。就如同下面这个计算文件数目的程序:
@echo off
rem 打开延迟扩展特性
setlocal enabledelayedexpansion
set /a n=0
for /f %%i in ('dir /b') do (
set /a n=!n!+1
)
echo 当前目录下的文件和目录总数为:%n%
endlocal
pause
似乎看到救星了,于是改变了一下原来的代码得到:
SETLOCAL
enabledelayedexpansion
SET
DirList
=
”C:
A” “C:
B” “C:
D E”
FOR
/
D %%m IN
(
%DirList%
)
DO
(
SET
DIR
=
%%m
FOR
/
R !
DIR
! %%i IN
(
*
)
DO
ECHO
%%i
)
ENDLOCAL
结果出来了,但是不全,只能出来第一个目录的所有文件。似乎!DIR!的改变还是不能影响第二个FOR的执行。再一次对shell表示失望!
最后的努力,来自于cell神奇的力量,求助函数。
@ECHO
off
SET
DirList
=
”C:
A” “C:
B” “C:
D E”
FOR
/
d %%m IN
(
%DirList%
)
DO
(
CALL
:GenerateList
%%m
)
GOTO
:EOF
:GenerateList
FOR
/
R %* %%i IN
(
*
)
DO
(
ECHO
%%i
)
GOTO
:EOF
J 终于还是笑到了最后!
另外还有一个命令可以遍历所有的文件:DIR /A:-D /B /S
因此得到解法二:
@ECHO
off
SET
DirList
=
”C:
A” “C:
B” “C:
D E”
FOR
/
d %%m IN
(
%DirList%
)
DO
(
CALL
:GenerateList
%%m
)
GOTO
:EOF
:GenerateList
DIR
%*
/
A:-D
/
B
/
S
GOTO
:EOF
3. 将结果输出了文件
在shell中一般采用重定向将结果输出到文件。可以考虑两种方式: append(>>), new(>);
考虑到使用append还需要在使用前清空文件,比较麻烦。因此还是使用后者。最终的结果如下:
@ECHO
off
CALL
: Transverse
>.
Data
cachePackagesFile
GOTO
:EOF
:Transverse
SET
DirList
=
”C:
A” “C:
B” “C:
D E”
FOR
/
d %%m IN
(
%DirList%
)
DO
(
CALL
:GenerateList
%%m
)
GOTO
:EOF
:GenerateList
FOR
/
R %* %%i IN
(
*
)
DO
(
ECHO
%%i
)
GOTO
:EOF
总结:看似简单一个任务,结果却耗费了颇多的精力才得以最终解决。虽然传说中的shell是超简单的脚本编程,但却是是用法颇多诡异之处,特别是相对于高级的编程语言。不过鉴于shell和windows命令的紧密邦定,能够熟练掌握shell编程应该还是有不少好处吧。
参考:
1.Windows.Shell.Script.Programming.for.the.Absolute.Beginner
2.跟我学CMD实战系列:
http://lifesinger.org/blog/?p=11
3.中国DOS联盟
http://www.cn-dos.net/forum/