本文目录
背景
佳能相机,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 获取我的电脑。