Windows shell 编程 ¬¬——将指定文件夹中的所有文件名输出到一个文件中

这篇博客介绍了如何使用Windows Shell脚本来遍历指定文件夹中的所有文件,并将文件名输出到一个文件中。作者通过解决存放文件夹路径、遍历文件和输出文件到文件的三个问题,展示了使用FOR循环、环境变量和延迟扩展等技巧。最终,通过CALL函数和DIR命令实现了目标,同时分享了在Shell编程中遇到的挑战和解决方案。
  问题分析:该任务主要有三个问题要解决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  / / 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/ 
 
(* 【功能说明】 此脚本根据用户设定的标签与文件夹映射关系,自动将带有特定标签的文件/文件夹 归类到对应的目标文件夹。适用于macOS的"自动操作"(Automator)。 *) -- ############ 用户可修改变量区域 ############ -- 标签与目标文件夹的映射关系(按需增减) set tagFolderMap to {"红色", "测试"} -- 源文件夹路径(需要监控的文件夹) set sourceFolderPath to "Macintosh HD:Users:ycz:Downloads:" -- 默认:下载文件夹 -- 可选替换路径示例: -- set sourceFolderPath to "Macintosh HD:Users:用户名:Desktop:" -- 替换为桌面路径 -- 目标根目录(归类文件夹的父目录) set baseDestinationPath to "Macintosh HD:Users:ycz:Downloads:测试:" -- 默认:文稿文件夹 -- set baseDestinationPath to "Macintosh HD:Users:用户名:归类目录:" -- 自定义路径 -- 是否处理子文件夹内容(true=处理, false=仅顶层) set includeSubfolders to true -- ############ 以下为脚本主逻辑(无需修改) ############ tell application "Finder" -- 检查并创建目标根目录 if not (exists folder baseDestinationPath) then make new folder at (path to documents folder) with properties {name:baseDestinationPath's last item} end if -- 获取源文件夹内容 set sourceFolder to folder sourceFolderPath set allItems to every item of sourceFolder -- 递归处理子文件夹(如果需要) if includeSubfolders then set subfolders to every folder of sourceFolder repeat with subfolder in subfolders set allItems to allItems & (every item of subfolder) end repeat end if -- 处理每个文件/文件夹 repeat with targetItem in allItems try -- 获取当前项目的标签 set itemTags to tag of targetItem -- 检查是否有标签 if itemTags is not {} then -- 匹配标签与目标文件夹 repeat with mapping in tagFolderMap set tagName to item 1 of mapping set folderName to item 2 of mapping -- 如果找到匹配标签 if itemTags contains tagName then -- 创建目标文件夹(如果不存在) set destFolderPath to baseDestinationPath & folderName if not (exists folder destFolderPath) then make new folder at folder baseDestinationPath with properties {name:folderName} end if -- 移动文件到目标文件夹 move targetItem to folder destFolderPath exit repeat -- 匹配后跳出当前循环 end if end repeat end if on error errMsg log ("处理失败: " & name of targetItem & " - " & errMsg) end try end repeat end tell display notification "文件归类完成!" with title "自动操作" 为什么我执行通过了,但是文件没有移动?
最新发布
10-14
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值