所以我正在编写一个脚本来移动给定目录中的重复文件(我们称之为D:\ Pictures)到Duplicates子目录(在这种情况下为D:\ Pictures \ Duplicates) 。
就目前而言,我唯一可见的问题是将文件从源(D:\ Pictures \ [picture])移动到目的地的实际行为(D:\ Pictures \ Duplicates \ [图片])。 Move-Item返回错误为"不支持给定路径的格式"。
用于重复文件的格式是对文件创建日期的简单重命名,例如:D:\ Pictures \ Duplicates \ 5-6-2015 06:34:22 PM.bmp
当我使用直接文件路径时,例如D:\ Pictures \ 001 - Copy.bmp和D:\ Pictures \ Duplicates \ 5-6-2015 06:34:22 PM.bmp它分别工作正常,只返回使用变量时的错误。
到目前为止我所得到的内容如下(包含用于测试复制目的的完整代码):
cls
echo "Duplicate Sorter -- Version 1.0.0 Alpha"
echo "-------------------------------------"
echo "What directory do you want to sort?"
$workingDir = "false"
while ($workingDir -eq "false")
{
$DIR = Read-Host
if (($DIR -eq "C:\") -or ($DIR -like "C:\Program Files*") -or ($DIR -like "C:\Program Files(x86)*") -or ($DIR -like "C:\Windows*") -or ($DIR -like "C:\DRIVERS*") -or ($DIR -like "C:\SWTOOLS*") -or ($DIR -like "C:\inetpub*") -or ($DIR -like "C:\PerfLogs*"))
{
echo "You can't choose that directory. Choose a different one."
}
Else
{
$workingDir = "true"
}
}
echo "What file type do you want to sort? (Press the relative number)"
echo "1. Pictures"
echo "2. Movies"
echo "3. GIFs"
echo "4. PNGs"
echo "5. Custom Type"
$workingType = "false"
while ($workingType -eq "false")
{
$fileType = Read-Host
if ($fileType -eq 1)
{
$fileType = "jpg"
$workingType = "true"
}
elseif ($fileType -eq 2)
{
$fileType = "avi"
$workingType = "true"
}
elseif ($fileType -eq 3)
{
#Pronounced with a hard G, not like Jif. Gifs aren't a brand of peanut butter!
$fileType = "gif"
$workingType = "true"
}
elseif ($fileType -eq 4)
{
$fileType = "png"
$workingType = "true"
}
elseif ($fileType -eq 5)
{
echo "Okay then, Mr. 'I'm special and deserve my own type', enter your custom type."
$fileType = Read-Host
$workingType = "true"
}
Else
{
echo "This is an invalid option. Try again."
}
}
$mkdups = "$DIR\$fileType Duplicates"
if (!(Test-Path -Path $mkdups))
{
New-Item $mkdups -ItemType directory
}
$preList = Get-ChildItem "$DIR" -Recurse
$list = $preList | Where-Object { $_.Extension -eq $fileType }
$list = $list | %{$_.FullName}
$hashedList=New-Object System.Collections.ArrayList
if ($hashedList.Length -gt 1)
{
$hashedList.Clear()
}
foreach ($i in $list)
{
$hashedList+=Get-FileHash $i
}
[int]$iterator1=0
foreach ($i in $hashedList)
{
[int]$iterator2=0
foreach ($j in $hashedList)
{
if(($i.Hash -eq $j.Hash) -and ($iterator2 -gt $iterator1))
{
if(Test-Path $j.Path)
{
$checkFile=Split-Path $j.Path -Leaf
$rnd=(Get-ChildItem $j.Path).CreationTime
$rnd=$rnd -Replace("/","-")
$checkFile="$rnd$fileType"
$dirPath=$mkdups
$j.Path="$dirPath\$checkFile"
$src=$i.Path
$dest=$j.Path
Move-Item -Path $src -Destination $dest #Problem Area
}
}
$iterator2++
}
$iterator1++
}
答案 0 :(得分:3)
您不能在文件名中使用冒号。
使用类似$dest.Replace(':','_')
的内容替换下划线。
答案 1 :(得分:3)
Mike Shepard是对的,问题是你的文件名中有冒号,这是不允许的。但我写信是为了帮助你......
将ForEach($j in $hashedList)
嵌套在ForEach($i in $hashedList)
内会让我的大脑受到太大伤害而无法发表评论。我还看到使用Switch
cmdlet更好地完成了If / ElseIf / ElseIf / ElseIf / ElseIf。此外,在已经有一个文件之后重新创建一个哈希数组,好吧,只是没有。最后,执行递归文件列表,然后过滤掉您想要的文件是一种浪费。为提供程序提供一个过滤器,以便它只返回您要处理的文件而不是所有文件,然后使PowerShell过滤掉额外的文件。提供程序做了一件事,它处理文件及其信息,并且它比PowerShell更好(通常),因此首选在该级别进行过滤。在这里,请考虑以下替代方案:
让我向您介绍Switch
。不,不是威尔史密斯的歌。这个伟大的小cmdlet将允许您定义各种情况,并将相应地执行代码。看看我们可以将你的If / ElseIf块减少到:
while ($workingType -eq "false")
{
Switch(Read-Host){
"1" {$fileType = "*.jpg";$workingType = 'true';Continue}
"2" {$fileType = "*.avi";$workingType = 'true';Continue}
"3" {$fileType = "*.gif";$workingType = 'true';Continue}
"4" {$fileType = "*.png";$workingType = 'true';Continue}
"5" {
"Okay then, Mr. 'I'm special and deserve my own type', enter your custom type."
$fileType = Read-Host "[Example: *.bat]"
$workingType = 'true'
Continue
}
default {"This is an invalid option. Try again."}
}
}
通过将$fileType
设置为"*.jpg"
而非"jpg"
,我们也为下一点做好了准备。
不是获取所有文件,而是使用-filter
参数获取所需文件。这会将过滤器直接传递给FileSystem提供程序,因此它只返回您想要的文件。不再有$prelist
和所有垃圾:
$List = Get-ChildItem "$DIR" -Filter $fileType -Recurse
既然您有一个要查看的文件列表,而不是将其转换为路径列表,请将它们保留为[FileInfo]对象,这样更有用!我们可以直接将Hash作为属性直接添加到每个对象:
$List | ForEach{Add-Member -InputObject $_ -NotePropertyName 'Hash' -NotePropertyValue (Get-FileHash $_.FullName | Select -Expand Hash)}
在那里,现在所有文件的Hash都包含在他们的对象中。现在神奇......没有将ForEach
循环嵌套在彼此之内。根据您列表中的对象数量,该练习会呈指数级增长。而是在Group-Object
属性上使用Hash
。然后过滤掉组中只有一个文件的任何地方,你就得到了重复的文件了!
$List | Group Hash | Where{$_.Count -gt 1}
它显示了您通过文件哈希分割的所有重复项。它返回具有Count
属性的分组对象,这是一个自解释的属性,Name
属性,它是您分组的任何值(在您的情况下,每个哈希值)和一个{ {1}}属性,它是与Group
匹配的所有内容的集合。所以从那里你移动每个组的文件,跳过第一个。类似的东西:
Name
那就在那里取代你的整个$List | Group Hash | Where{$_.Count -gt 1} | ForEach{
$_.Group | Select -Skip 1 | Move-Item -Dest $mkdups
}
循环及其中的所有内容。所以现在让我们把它们放在一起:
ForEach($i in $hashlist)
有关我验证目录的说明,请参阅:https://regex101.com/r/jB0yD9/1