I have files the following files in my path
ENG0100100VOL-815299004001
ENG0100100VOL-815299004002
ENG0100100VOL-815299004003
ENG0100100VOL-815299004004
ENG0100100VOL-815300004001
ENG0100100VOL-815300004002
ENG0100100VOL-815300004003
ENG0100100VOL-815300004004
I want to
create unique folder(s) with the prefix "VOL-" followed by 6 digits identifier and move the respective files of the package to that folder (VOL-81529 and VOL-81530). Ideally its 4 files per folder. NOTE: These values may change overtime and the file numbers are not fixed.
Folder VOL-81529 should contain files
ENG0100100VOL-815299004001
ENG0100100VOL-815299004002
ENG0100100VOL-815299004003
ENG0100100VOL-815299004004
Folder VOL-81530 should contain files
ENG0100100VOL-815300004001
ENG0100100VOL-815300004002
ENG0100100VOL-815300004003
ENG0100100VOL-815300004004
Traverse through the folders and unzip the file with only suffix 002 and 004 (last three digits) of the 12 digits. And remove all the package files from the folder after unzipping
Folder VOL-81529
ENG0100100VOL-815299004002
ENG0100100VOL-815299004004
Folder VOL-81530
ENG0100100VOL-815300004002
ENG0100100VOL-815300004004
I have little knowledge in coding, I have written this and its requires some additional inputs
$files = Get-ChildItem "\My Documents\Path\files" | ForEach-Object{$_.Tostring().substring(14,5)}
$max =$files.count
for($i=0,$i -lt $max, $i++){if ($files[$i] -eq $files[$i+1]) { $value = $files[$i] } else { $value = $files[$i+1]}}
If you only need the files ending with 002 or 004 we will only treat this ones, right?
This code checks for the desired ending of the file and extracts the content of the archives to a subfolder of the target folder. After the extraction it deletes the files. ... and the other files as well. ;-)
$Path = '\My Documents\Path\files'
Get-ChildItem -Path $Path |
ForEach-Object {
$ID = ($_.BaseName -split '-')[1].Substring(0, 5)
$TargetPath = Join-Path -Path $_.Directory -ChildPath ('VOL-' + $ID)
if ( -not (Test-Path -Path $TargetPath)) {
New-Item -Path $TargetPath -ItemType Directory | Out-Null
}
if ($_.BaseName -match '002$|004$') {
$RenamedItem = Rename-Item -Path $_.FullName -NewName ($_.BaseName + '.zip') -PassThru
$DestinationPath = Join-Path -Path $TargetPath -ChildPath $_.BaseName
Expand-Archive -Path $RenamedItem.FullName -DestinationPath $DestinationPath
Remove-Item -Path $RenamedItem.FullName
}
else {
Remove-Item -Path $_.FullName
}
}
Related
The code below has been wonderful so far for organising my hard-drives.
I do face this error when I transfer large amounts of data:
Move-Item : Cannot create a file when that file already exists.
This happens when I move a file that is duplicate, is there a way to rename the duplicate file in some sort of sequence?
That would be much appreciated :))
# Get all files
Get-ChildItem "C:\zAa" -File -Recurse | ForEach-Object {
# Get the modified date
$dt = Get-Date $_.LastWriteTime
$year = $dt.Year
$month = $dt.Month
# This adds "0" in front of the 1-9 months
if($dt.Month -lt 10) {
$month = "0" + $dt.Month.ToString()
} else {
$month = $dt.Month
}
# Remove leading '.' from the extension
$extension = $_.Extension.Replace(".", "")
# Where we want to move the file
$destinationFolder = "C:\zBb\$extension\$year\$month\"
# Ensure full folder path exists
if(!(Test-Path $destinationFolder)) {
New-Item -ItemType Directory -Force -Path $destinationFolder
}
# Copy/Move the item to it's new home
Move-Item $_.FullName $destinationFolder
}
I haven't been able to do much, I normally go find the duplicates and rename them manually.
Probably the easiest way to move a file with a unique name is to use a Hashtable that stores the filenames already present.
Then a simple loop can add a sequence number to its file name until it is no longer found in the Hashtable.
Next simply move the file under that new name.
Your code modified:
# Where we want to move the file
$destinationFolder = 'C:\zBb\{0}\{1:yyyy}\{1:MM}' -f $_.Extension.TrimStart("."), $_.LastWriteTime
# Ensure full folder path exists
$null = New-Item -Path $destinationFolder -ItemType Directory -Force
# create a Hashtable and store the filenames already present in the destination folder
$existing = #{}
Get-ChildItem -Path $destinationFolder -File | ForEach-Object { $existing[$_.Name] = $true }
# Get all source files
Get-ChildItem "C:\zAa" -File -Recurse | ForEach-Object {
# Copy/Move the item to it's new home
# construct the new filename by appending an index number in between brackets
$newName = $_.Name
$count = 1
while ($existing.ContainsKey($newName)) {
$newName = "{0}({1}){2}" -f $_.BaseName, $count++, $_.Extension
}
# add this new name to the Hashtable so it exists in the next run
$existing[$newName] = $true
# use Join-Path to create a FullName for the file
$newFile = Join-Path -Path $destinationFolder -ChildPath $newName
Write-Verbose "Moving '$($_.FullName)' as '$newFile'"
$_ | Move-Item -Destination $newFile -Force
}
I Have this powershell script “copFiles.ps1” that looks in a txt file "Filestocopy.txt" for a list and copies them to a destination
$source = "C:\Data\Filestocopy.txt"
$destination = "C:\Data\Models"
Get-Content $source | ForEach-Object {copy-item $_ $destination}
It’ll only copy the files if they’re in the same folder as the .ps1 file and it ignores subfolders, how can I get it to look in subfolders of the folder that its in, I gather I need to use the -recurse option but don’t know how to rewrite it so it works.
The .ps1 file is fired by a bat file.
Many thanks
I don't know how fast this will be, but you can give an array as the argument for the -Path parameter of Get-ChildItem add the -Recurse switch to dig out the files in subdirectories and simply pipe them along to Copy-Item. something like:
Get-ChildItem (Get-Content $Source) -Recurse |
Copy-Item -Destination $destination
You may also want to add the -File switch.
Update
Based on your comment I played around with this a a little more:
$source = "C:\Data\Filestocopy.txt"
$Destination = "C:\data\Models"
# Get-ChildItem (Get-Content $Source) -Recurse |
Get-ChildItem (Get-Content $Source) -Recurse -File |
ForEach-Object{
If( $_.Directory.FullName -eq $Destination )
{ # Don't work on files already present in the destination
# when the destination is under the current directory...
Continue
}
$FileNum = $null
$NewName = Join-Path -Path $Destination -ChildPath $_.Name
While( (Test-Path $NewName) )
{
++$FileNum
$NewName = Join-Path -Path $Destination -ChildPath ($_.BaseName + "_" + $FileNum + $_.Extension)
}
Copy-Item $_.FullName -Destination $NewName
}
This will increment the destination file name in cases where a file by that name already exists in the destination. If the destination is under the current directory it will prevent analyzing those files by comparing the path of the file to the destination. Files must have unique names in a given folder so I'm not sure how else it can be handled.
I have a folder with a number of pdf files whereby the last 8 characters is the date of the document in the format YYYYMMDD, samples are:
File_YYYYMMDD
Test_Ref_YYYYMMDD
file.nr_YYYYMMDD
I am looking for a way to create a folder structure based on last 8 characters:
OutputFolder\Subfolder YYYY\Subfolder MM\File name remains the same.
Thanks.
For this you need to iterate through the source folder where the pdf files are, and using their BaseName (= filename without extension), get the year and month part.
Having those, creating the folder structure is a breaze in PowerShell:
$sourceFolder = 'D:\OriginalPath' # the path where your pdf files are
$destination = 'D:\PdfFiles' # the folder where the new folder structure should be created
# get the pdf files that have a basename ending with 8 digits
Get-ChildItem -Path $sourceFolder -Filter '*.pdf' -File |
Where-Object { $_.BaseName -match '\d{8}$' } |
ForEach-Object {
# split the year and month part of the file's basename
$match = $_.BaseName -match '(\d{4})(\d{2})\d{2}$'
$year, $month = $matches[1..2]
# construct the folder path
$targetFolder = Join-Path -Path $destination -ChildPath "$year\$month"
# if the target folder does not exist, create it
if (!(Test-Path -Path $targetFolder -PathType Container)) {
$null = New-Item -Path $targetFolder -ItemType Directory
}
# you probably now want to copy or move the file in that target folder
$_ | Copy-Item -Destination $targetFolder
}
Example of the source folder:
D:\ORIGINALPATH
File_20200201.pdf
File_20200327.pdf
Test_Ref_20191127.pdf
After running the code:
D:\PDFFILES
+---2019
| \---11
| Test_Ref_20191127.pdf
|
\---2020
+---02
| File_20200201.pdf
|
\---03
File_20200327.pdf
I currently have 20000+ folders that where given a random string of characters when created. I would like to rename each folder with the name of the last PDF modified within each folder. I'm definitely in over my head. The current script seems to just move the PDF and/or folder without renaming it or creating a folder with the PDF name.
Get-ChildItem -Path $SourceFolder -Filter *.pdf |
ForEach-Object {
$ChildPath = Join-Path -Path $_.Name.Replace('.pdf','') -ChildPath $_.Name
[System.IO.FileInfo]$Destination = Join-Path -Path $TargetFolder -ChildPath $ChildPat
if( -not ( Test-Path -Path $Destination.Directory.FullName ) ){
New-Item -ItemType Directory -Path $Destination.Directory.FullName
}
Copy-Item -Path $_.FullName -Destination $Destination.FullName
}
Welcome, Robert! There's a few things going on with your script:
There's a typo: $ChildPat
You don't need a FileInfo object to create the new directory, and you can't create one from a non-existent path. $Destination = Join-Path $_.Directory $_.BaseName will get the new folder name more reliably, in the unusual case where the file name has embedded '.pdf'
It doesn't get the latest PDF.
Assuming you only want to get folders that have a PDF, you should have a nested Get-ChildItem for each folder, as #Lee_Dailey recommended:
Push-Location $SourceFolder
Foreach ($dir in (Get-ChildItem *.pdf -Recurse | Group-Object Directory | Select Name )){
Push-Location $dir.Name
$NewestPDF = Get-ChildItem *.pdf | Sort-Object ModifiedDate | Select -Last 1
$Destination = Join-Path $dir.Name "..\$($NewestPDF.BaseName)"
If(!(Test-Path $Destination)){New-Item $Destination -ItemType Directory}
Copy-Item *.PDF $Destination
Pop-Location
#Remove-Item $dir.Name #uncomment to remove the old folder (is it empty?)
}
All my files are in specific folders:
17\1\1\PRO
17\1\2\PRO
17\2\1\PRO
xx\xx\xx\PRO
17 is the year (so 18 for next year etc)
the first 1 is the folder specifying the case number (can be up to 100).
The second 1 is the sub parts on the case number.
That last 1 has a folder PRO in it where all data resides.
We need to move these files, but the files need to stay inside their respective "PRO" folders.
For example:
a file in 17\1\1\pro\xxx\www\ needs to go to 17\1\1\pro\movies
a file in 17\2\2\pro\xxdfsdf\eeee\ needs to go to 17\2\2\pro\movies.
The movies folder should get created if there are files to move.
I need to get a part of the full name of a file and move the file there to the "movie" folder. The problem is I do not know how to split the full name, add \movies to it and move the files there.
This is my code so far:
Get-ChildItem -Path $mypath -Recurse -File -Filter $extension | select $_Fullname |
Move-Item -Force -Destination ($_Fullname.Split("pro"))
If the destination is always "movies subdirectory of the grandparent directory of the file's directory" you can build the destination path relative to the file's location:
Get-ChildItem ... | ForEach-Object {
$dst = Join-Path $_.Directory '..\..\movies'
if (-not (Test-Path -LiteralPath $dst -PathType Container)) {
New-Item -Type Directory -Path $dst | Out-Null
}
Move-Item $_.FullName -Destination $dst
}
If the PRO directory is your anchor you could use a regular expression replacement like this instead:
Get-ChildItem ... | ForEach-Object {
$dst = $_.Directory -replace '^(.*\\\d+\\\d+\\\d+\\PRO)\\.*', '$1\movies'
if (-not (Test-Path -LiteralPath $dst -PathType Container)) {
New-Item -Type Directory -Path $dst | Out-Null
}
Move-Item $_.FullName -Destination $dst
}
If you don't know how many directories there are, I would do something like this:
Get-ChildItem -Path $mypath -Recurse -File -Filter $extension | ForEach-Object {
if ($_.FullName.IndexOf('\PRO\') -gt 0) {
$Destination = Join-Path -Path $_.FullName.Substring(0,$_.FullName.IndexOf('\PRO\') + 5) -ChildPath 'movies';
New-Item $Destination -ItemType Directory -ea Ignore;
$_ | Move-Item -Destination $Destination;
} else {
throw ("\PRO\ path not found in '$($_.FullName)'");
}
}
This will work fine as long as your paths only have \pro\ once. If they have it more than once like customer\pro\17\pro\17\1\1\pro\xx\yy\zz\www and you need the last index, then use $_.FullName.LastIndexOf('\pro\').
If you've got \pro\ directories both before and after the directory that .\pro\movies\ is in, well, you're in trouble. You'll probably have to find a different point of reference.
With a set of folders
17\1\1\PRO
17\1\2\PRO
17\2\1\PRO
You could try the following
$RootPaths = Get-ChildItem -Path C:\folder\*\*\*\pro
$RootPaths will then contain all 3 paths mentioned above and the code below will move all files to the appropriate directory.
ForEach( $Path in $RootPaths)
{
$Movies = Join-Path $Path -Child "Movies"
If( -not (Test-Path $Movies ) ) { New-Item -Path $Movies -ItemType Directory }
Get-ChildItem -Path $Path -Recurse -File -Filter $Extension |
Move-Item -Path $_.FullName -Destination "$( $Path )\Movies"
}
This way it doesn't matter how many levels down your files are. They always get moved to the same directory.