我有一个文件夹结构,其中包含以下格式的音乐文件mp3
,flac
文件夹结构如下所示:
S:\Music\
|___\ABBA
|___\...
|___\ZZTop
我正在尝试创建一个 Powershell 脚本来选择随机从目录(包括其子目录)中取出 50 个文件Music
并将其放入另一个文件夹中。
我尝试过这个脚本,但是它不起作用。
$d = gci "S:\Music\*.mp3" | resolve-path | get-random -count 50
Copy-Item $d -destination 'S:\Music\RandomFIles'
它抛出这个错误:Copy-Item : Cannot bind argument to parameter 'Path' because it is null.
答案1
为什么需要| Resolve-Path |
?您需要将开关附加-File
到 Get-ChildItem,这样您就不会同时检索 DirectoryInfo 对象和 FileInfo 对象。
.FullName
然后在参数上使用该属性-Path
(这是第一个需要字符串数组的位置参数)
参见复制项目
根据音乐收藏的大小,这可能需要一些时间才能完成
通过使用文件夹内的目标文件夹来递归迭代文件,您可能会遇到异常:
“复制项目:无法用其自身覆盖项目 blah.mp3。”
为了避免这种情况,您需要附加-ErrorAction SilentlyContinue
到 Copy-Item 命令。
以下复制 .MP3 和 .FLAC 文件:
# loop through the music folders and search for the file patterns you need to copy
$files = Get-ChildItem -Path 'S:\Music' -File -Include '*.mp3', '*.flac' -Recurse | Get-Random -Count 50
Copy-Item -Path $files.FullName -Destination 'S:\Music\RandomFiles' -Force -ErrorAction SilentlyContinue
但是,如果您只想复制 .MP3 文件,请注意该参数的-Filter
工作速度比-Include
但更快..
-Filter
仅允许一种文件名模式,如“*.mp3”-Include
允许使用名称模式数组,但仅当路径以 结尾时才能使用\*
,或者当-Recurse
还使用 switch时
# loop through the music folders and search for the single file pattern you need to copy
$files = Get-ChildItem -Path 'S:\Music' -File -Filter '*.mp3' -Recurse | Get-Random -Count 50
Copy-Item -Path $files.FullName -Destination 'S:\Music\RandomFiles' -Force -ErrorAction SilentlyContinue
在这两种情况下,请确保目标路径存在。
编辑
正如您所发现的,多次运行第一个代码块,每次运行后,总数可能会少于 50 个项目。这是因为Get-Random -Count 50
可以返回目标文件夹中已有的文件,而上面的代码只是简单地复制它们。
此外,对于我来说,它本身是否不返回重复项尚不清楚Get-Random -Count 50
。到目前为止,我还没有看到这种情况,但返回的 50 个项目可能并不总是唯一的。
为了解决这个问题,下面确保每个复制的文件都还未在目标文件夹中找到,并且它将始终返回 50 个没有重复的文件。
但有一个警告:
当反复运行时,如果源文件夹中没有尚未复制的文件,则可能会陷入无限循环。
$sourceFolder = 'S:\Music'
$destination = 'S:\Music\RandomFiles' # this folder MUST already exist
# first create a Hashtable with file already in the destination folder (Names only)
$existing = @{}
Get-ChildItem -Path $destination -File -Filter '*.mp3' | ForEach-Object { $existing[$_.Name] = $true }
# next loop through the music folders and search for the single file pattern you need to copy
# if the destination folder is NOT in the sourcefolder, you can leave out the Where-Object clause
$files = Get-ChildItem -Path $sourceFolder -File -Filter '*.mp3' -Recurse | Where-Object{ -not $_.DirectoryName.StartsWith($destination) }
# make sure you do not exceed the total number of files
$totalFiles = @($files).Count
$maxFiles = [math]::Min($totalFiles, 50)
# start a counted loop
for ($i = 0; $i -lt $maxFiles; $i++) {
$rnd = Get-Random -Maximum $totalFiles # get a random array index from the complete $files collection
if ($existing.ContainsKey($files[$rnd].Name)) { $i-- } # file is already present; decrease the counter
else {
Write-Host "Copying file no. $($i + 1)/$maxFiles"
$files[$rnd] | Copy-Item -Destination $destination # copy the file
$existing[$files[$rnd].Name] = $true # and add its name to the Hashtable
}
}