Hi,
I am trying to achieve following with powershell script:
Got folder A where txt and png files are placed (1.txt, 1.png, 2.txt, 2.png etc). I want powershell script to do following:
- search for ready.txt file in folder
- when ready file is there retrieve list of files to zip from ready.txt count them and check if that matches count of txt and png in folder
- create subfolders and move files there: packages for 1000 each but pairs have to be in same folders: 1.txt have to be in folder with 1.png
- zip all and move to folder
I am stuck on 3 and 4. Guess proper loop is needed. What i got so far:
$location=“C:\A”
$strFileName=“ready.txt”
$archive=“C:\B”
function fileready
{
while (!(Test-Path $location$strFileName)) {Start-Sleep -s 5}
}
$readyfile = Get-ChildItem -Recurse -include $strFileName
[int]$readyfilecount = (Get-Content $readyfile | Measure-Object).Count
$readyfilecount
[int]$folderfilescount = (Get-ChildItem -Recurse -path $location -include @(“.txt",".png”)).Count
$folderfilescount
If ($readyfilecount -eq $folderfilescount)
{
}
else
{
fileready
}
Thx for any suggestions!
A part of your question confuses me. Does this ready.txt file contain just a list of filenames or paths that need to be moved?
This will create folders, move files to specific folders, and create a zip archive for every 1000 folders containing your files. This script uses 7zip Portable to create the archives. I now realize how powerful and easy a switch statement is compared to cumbersome nested If/foreach statements. Thank you for the challenge.
$source = "C:\FolderA\"
$dest = "C:\FolderB\"
$files = Get-ChildItem -Path $source -Recurse -Include "*.png","*.txt","readyfile.txt"
$readyfile = $files -match 'readyfile.txt'
$7zip = Read-Host 'Where is 7zip (7z.exe)?'
# Create folders and move files to folders
Write-Verbose 'Copying files to new folders' -Verbose
foreach ($file in ($files -notmatch 'readyfile.txt')){
$basename = "$source$($file.basename)"
new-item -ItemType Directory $basename -ErrorAction SilentlyContinue
move-item -Path $file.FullName -Destination $basename -ErrorAction SilentlyContinue}
# Cast and sort folder names (numbers)
Write-Verbose 'Getting list of new folders' -Verbose
$folders = Get-ChildItem -Path $source -Directory -Recurse
$fsort = $folders | foreach {$_.Name -as [int]} | Sort-Object
# Add folders to zip archives
switch ($fsort){
1 {$dir = 1000; If (!(Test-Path "$($source)1000")){
New-Item -ItemType Directory "$($source)1000" -ErrorAction SilentlyContinue}}
{($_ % 1000 -eq 0) -or ($_ -eq 1000)}
{$dir = $_; Start-Process $7zip -ArgumentList "a `"$dest$dir.zip`" `"$source$_`"" -WindowStyle Hidden -Wait}
{(($_ -le 1000) -and ($_ % 1000 -ne 0)) -or (($_ -gt 1000) -and ($_ % 1000 -ne 0))}
{Write-Verbose "Copying folder $_ to archive" -Verbose
Start-Process $7zip -ArgumentList "a `"$dest$dir.zip`" `"$source$_`"" -WindowStyle Hidden -Wait}
}
You can use the builtin COM shell.application object, but I found it to be limited in function. I could not suppress COM progress bars, force asynchronous transfer (wait for previous file transfer), etc. Below is an example of what I tried with COM.
$shell = ( new-object -com shell.application ).NameSpace( $folder.FullName )
$zipshell = ( new-object -com shell.application ).NameSpace( $ziplocation )
$zipshell.CopyHere($folder.FullName)
There are a couple of other options for creating the zip file:
PowerShell version 5 adds native support for ZIP files.
For older versions, the PowerShell Community Extensions (pscx) can be used to add ZIP file support by way of the Write-ZIP cmdlet.
Hi,
Thx for replies. To clarify this: there is a txt file with only filenames to zip. Lets say there is 2500 filenames there, It should only create 3 subfolders and zip them. So for 1000 files create subfolder, another 1000 files create subfolder, last 500 files create subfolder. Will have a look random commandline on your code.
Regards
I understand now. I modified my script above, this should work for you. Output will display what files were moved and if a filename in the text does not exist in the folder.
I choose to use the builtin ZipFile .NET class discussed here. The page states the .NET class was introduced with .NET 4.5, but it worked with my 4.0 install.
# Get files
$source = "C:\A"
$movefolders = "C:\A\MoveFolders"
$dest = "C:\B"
Write-Verbose "Getting list of files" -Verbose
$files = Get-ChildItem -Recurse -Include "*.png","*.txt" -Path $source
$ready = Get-ChildItem $source -Filter 'ready.txt'
# Move every 1000 files to a folder
$count = 0 ; $readyfile = $ready.OpenText()
while ($readyfile.EndOfStream -eq $false){
switch ($readyfile.ReadLine()){
$_ {$count++;$num=$_; If ($count -eq 1){$newdir = 1000 ;
New-Item -ItemType Directory -Path "$movefolders\$newdir" -ErrorAction SilentlyContinue}}
# Match each line in ready.txt with files
$_ {If ($match = $files | where {$_.BaseName -eq $num}){$match |
move-Item -Destination "$movefolders\$newdir" ;
Write-Verbose "Moving file $num to $movefolders\$newdir !!!" -Verbose} Else
{Write-Verbose "$num not found" -Verbose}}
$_ {If ($count % 1000 -eq 0){$newdir = $count+1000 ;
new-item -ItemType Directory -Path "$movefolders\$newdir" -ErrorAction SilentlyContinue}}
} # End switch
} # End while
# Add folders to zip archive
add-type -AssemblyName 'System.IO.Compression.filesystem'
Write-Verbose "Creating archive" -Verbose
[System.IO.Compression.ZipFile]::CreateFromDirectory($movefolders,"$dest\Archive.zip")