本文目录
背景
佳能相机,usb数据线接win7系统以后,每次都要进入比较深的目录下,而且每次都要手动复制/粘帖照片和视频文件到本地硬盘上。麻烦。(另外,不喜欢用读卡器+存储卡的方式)。
如果能做个软件,运行下就全自动copy到d盘某个目录下,是不是很方便呢?这功能,脚本或编程,不知道能否实现呢?简单点说就是:(读取相机储存卡里的相片,并保存到电脑的某个磁盘的某个目录里面,并以当时的日期作为文件夹名称保存)
需求
- 手工运行1次软件,首先软件会在d盘根目录下建立1个新目录。目录名字就是:年-月-日,比如:2014-06-27
- 软件会进行检测,如果没有满足条件,就直接退出。(软件检测过程,是有难度的吧?因为没有盘符)
- 如果有满足条件的照片/视频,不需要提示(不需要键盘/鼠标参与,不需要弹出对话框),就直接copy所有的照片/视频文件,可以显示文件copy复制过程界面,复制文件完成后退出。复制过程,尽量避免出现软件界面,避免用鼠标/键盘操作选择才能复制。
- 判断是否存在DCIM这个目录?如果存在,就搜索里面是否有照片/视频,并执行下面操作
a、copy DCIM目录下所有照片和视频文件,到d盘下的新目录(/y参数是指覆盖前不提示)。
b、copy完毕,删除DCIM源目录下的所有照片/视频文件。运行过程中,尽量别弹出交互式菜单/选择项目啥的。
c、DCIM目录下还有别的不同级别的子目录,这些子目录下面才会有照片和视频文件,文件扩展名主要是jpg avi mov mp4 mts
知识点分析
- 该问题的主要难点是以MTP方式连接的设备没有盘符,不能使用简单的copy命令。不过,此问题由@codecook和@小楼在Powershell从数码相机复制图片思路 一文中完美解决:使用shell.application即可。
- 为了增强脚本的复用性,准备简单实现Get-ShellItem,Get-ChildShellItem,Copy-ShellItem,Remove-ShellItem,Move-ShellItem 5个函数,这样即使脱离了这个需求,本篇的脚本仍然有意义。
- 调用过程中可能会大量使用Shell.Application 对象,觉得将它作为全局变量。
- 默认的 NameSpace()方法只支持目录,不支持文件文件。否则会抛出异常:使用“1”个参数调用“NameSpace”时发生异常:“未指定的错误 (异常来自 HRESULT:0x80004005 (E_FAIL))”。 为了增强兼容性和保持一致性,如果传入一个文件路径,可以尝试通过文件路径获取目录,然后再从目录的子项中获取对应的Shell项。
- Get-ChildShellItem只有支持递归才会有意义,但是直接递归非常容易引发系统堆栈层数不足的异常,所以采用用户自定义栈。
- 如果不想复制文件和移动文件时出现进度条,Copyhere和MoveHere中的参数16改成4
ShellItem源脚本
# # 获取 Shell.Application 代理 # function Get-ShellProxy { if( -not $global:ShellProxy) { $global:ShellProxy = new-object -com Shell.Application } $global:ShellProxy } # # 查看 Shell 项 # function Get-ShellItem { param($Path=17) $shell=Get-ShellProxy # 默认的 NameSpace()方法只支持目录,不支持文件 # 为了增强兼容性和保持一致性,如果传入一个文件路径,可以尝试通过文件路径获取目录,然后再从目录的子项中获取Shell项 trap [System.Management.Automation.MethodInvocationException] { $Path = $Path.ToString() $dir = $Path.Substring( 0 ,$Path.LastIndexOf('\') ) return $shell.NameSpace($dir).items() | where { (-not $_.IsFolder ) -and $_.path -eq $Path} | select -First 1 continue } $shell.NameSpace($Path).self } # # 查看 Shell 子项,支持递归和过滤 # function Get-ChildShellItem { param( $Path=17, [switch]$Recurse, $Filter=$null) #内部过滤器 Filter myFilter { if($Filter){ $_ | where { $_.Name -match $Filter } } else{ $_ } } $shellItem = Get-ShellItem $Path $shellItem | myFilter # 如果是目录 if( $shellItem.IsFolder ){ # 如果指定递归 if($Recurse) { $shellItem | myFilter $stack=New-Object System.Collections.Stack # 当前目录压入堆栈 $stack.Push($shellItem) while($stack.Count -gt 0) { # 访问栈顶元素 $top = $stack.Pop() $top | myFilter # 访问栈顶元素的子元素 $top.GetFolder.items() | foreach { if( $_.IsFolder ) { $stack.Push($_) } else { $_ | myFilter } } } } else{ $shellItem.GetFolder.items() | myFilter } } } # # 复制Shell项 # function Copy-ShellItem { param($Path,$Destination) $shell=Get-ShellProxy $shell.NameSpace($Destination).Copyhere($Path,16) } # # 删除Shell项 # function Remove-ShellItem { param($Path) $ShellItem = Get-ShellItem $Path $ShellItem.InvokeVerb('delete') } # # 移动Shell项 # function Move-ShellItem { param($Path,$Destination) $shell=Get-ShellProxy $shell.NameSpace($Destination).MoveHere($Path,16) }
简单测试
Get-ShellItem 获取我的电脑
PS> Get-ShellItem Application : System.__ComObject Parent : System.__ComObject Name : 这台电脑 Path : ::{20D04FE0-3AEA-1069-A2D8-08002B30309D} GetLink : GetFolder : System.__ComObject IsLink : False IsFolder : True IsFileSystem : False IsBrowsable : False ModifyDate : 1899/12/30 0:00:00 Size : 0 Type : 系统文件夹
Get-ChildShellItem 获取我的电脑下的通用文件夹和驱动器
S> Get-ChildShellItem | select name,path Name Path ---- ---- 这台电脑 ::{20D04FE0-3AEA-1069-A2D8-08002B30309D} 音乐 I:\Users\非苔\Music 下载 I:\Users\非苔\Downloads 图片 I:\Users\非苔\Pictures 视频 I:\Users\非苔\Videos 文档 I:\Users\非苔\Documents 桌面 I:\Users\非苔\Desktop A820t ::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\\?\us... 系统 (C:) C:\ 程序 (D:) D:\ 学习 (E:) E:\ DVD RW 驱动器 (F:) F:\ 娱乐 (G:) G:\ win8 (I:) I:\
将我手机中的外置存储卡中的DCIM下的照片和视频按日期复制在D:\DICM目录下
A820T是什么东西,是我的联想手机啦。
$des = 'D:\DICM' $phone = Get-ChildShellItem | where { $_.name -eq 'a820t' } Get-ChildShellItem -Path "$($phone.Path)\外置存储卡\DCIM" -Filter '(.jpg)|(.3gp)$' | foreach { #获取照片创建日期 $datestr = $_.Parent.GetDetailsOf($_,3) $datestr = ([datetime]$datestr).ToString('yyyy-MM-dd') #新建日期文件夹 $dir = Join-Path $des $datestr if( -not (Test-Path $dir) ) { mkdir $dir } # 复制文件 Copy-ShellItem -Path $_ -Destination $dir # 或者移动文件 # Move-ShellItem -Path $_ -Destination $dir }
本文链接: https://www.pstips.net/access-file-system-against-mtp-connection.html
请尊重原作者和编辑的辛勤劳动,欢迎转载,并注明出处!
请尊重原作者和编辑的辛勤劳动,欢迎转载,并注明出处!
在网上搜过,全网也找不到这个最终解决方案 集体的智慧
Hi,
请问要如何在“我的电脑”下开启powershell?我只能切换到C盘或D盘这样的盘符,无法用Get-ShellItem 获取我的电脑。
HI
是否可以提供完整的powershell脚本来运行。
谢谢。
Hi,
请问要如何在“我的电脑”下开启powershell?我只能切换到C盘或D盘这样的盘符,无法用Get-ShellItem 获取我的电脑。
Hi,
请问要如何在“我的电脑”下开启powershell?我只能切换到C盘或D盘这样的盘符,无法用Get-ShellItem 获取我的电脑。