I have to move files from a folder corresponding to a CSV column like that :
id
name
1a
file1
2b
file2
3c
file3
4d
file4
So if in my folder I have a file named file1 I need to create a folder named 1a and put file1 inside.
I have the following script
$CsvId = Import-Csv './test.csv'
$sourcePath= "C:\Users\olong\Desktop\object"
$dirPath = "C:\Users\olong\Desktop\dir"
$files = Get-ChildItem -Path $sourcePath | ForEach-Object -Process {[System.IO.Path]::GetFileNameWithoutExtension($_)}
$pattern = '(?<=_).*(?=_)'
ForEach ($item In $CsvId) {
$objectID = $item.'id'
$docID = $item.'name'
$location = $dirPath+ "\" + $objectID
Foreach ($file in $files) {
if($file -contains $docID) {
if (-not (Test-Path -Path $location)) {
mkdir -Path $location
}
Move-Item ($file.FullName) -Destination $location -Verbose -Force
}
}
}
But it gives me that error :
Move-Item : Cannot bind argument to parameter 'Path' because it is null.
At C:\Users\olong\Desktop\scriptMove.ps1:19 char:15
+ Move-Item ($file.FullName) -Destination $location -Verbose -Force
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Move-Item], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.MoveItemCommand
With PowerShell's debugger $file is not null and with or without parenthesis on $file.FullName does nothing
I think what you want to do is to get the file(s) as mentioned in the csv under header 'name' having any extension into a subfolder as mentioned in field 'id' and create that subfolder first if it does not already exist.
If my assumtion is correct, you can try below:
$sourcePath = "C:\Users\olong\Desktop\object"
$destinationPath = "C:\Users\olong\Desktop\dir"
Import-Csv -Path './test.csv' | ForEach-Object {
$targetDirectory = Join-Path -Path $destinationPath -ChildPath $_.id
# create the output subfolder if it does not already exist
$null = New-Item -Path $targetDirectory -ItemType Directory -Force
# next use the file basename from the $_.name column as filter and move the file(s)
Get-ChildItem -Path $sourcePath -Filter "$($_.name).*" -File | Move-Item -Destination $targetDirectory
}
Related
Renaming files from a CSV
I am trying to write a script to reorganize files in a folder structure using csv as index file but I canĀ“t figure out in how to solve the Rename-Item error.
Questions
Is there others way to write this script in order to achieve the same results more easily?
How to pass the right parameters to Rename-Item?
My csv file template
folderName newName oldName
---------- ------- -------
01 Course Overview 01_Course_Overview 1280x720.mp4
02 Introduction to PowerShell 01_Introduction to PowerShell 1280x720 (1).mp4
02 Introduction to PowerShell 02_Who Is This Course For? 1280x720 (2).mp4
02 Introduction to PowerShell 03_What Is PowerShell? 1280x720 (3).mp4
02 Introduction to PowerShell 04_Windows PowerShell and PowerShell 7 1280x720 (4).mp4
PowerShell Script
$csv = Import-Csv '.\index.csv' -Delimiter ';'
$newFolders = $csv.folderName | Sort-Object -Unique
$listFolders = Get-ChildItem -Directory | Select-Object Name
$listFiles = Get-ChildItem | Where {$_.extension -eq ".mp4"}
ForEach ($a in $newFolders){
If ($listFolders.Name -contains $a){
Write-Host "The Folder $a exist"
}
else{
New-Item -Path $pwd.Path -Name $a -Type Directory | Out-Null
Write-Host "The folder $a has been created"
}
}
ForEach ($b in $csv){
If ($listFiles.Name -contains $b.oldName){
Write-Host "File $($b.oldName) exist"
Write-Host "Renaming file to: "$($b.newName)"
#Rename-Item $($b.oldName) -NewName $($b.newName)
#Write-Host "Moving file to: "$($b.folderName)"
#Move-Item .\$($b.newName) -Destination .\$($b.folderName)
}
else{
Write-Host "File $($b.oldName) doesn't exist" `n
}
}
Error when executin Rename-Item
No D:\Downloads\Pluralsight\_PowerShell_Essentials\01_Powershell_Getting_Started\Temp\indexfiles.ps1:30 caractere:9
+ Rename-Item $($b.oldName) -NewName $($b.newName)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (D:\Downloads\Pl...280x720 (2).mp4:String) [Rename-Item], ArgumentException
+ FullyQualifiedErrorId : RenameItemArgumentError,Microsoft.PowerShell.Commands.RenameItemCommand
Here you have an example of how this can be done, this is mainly a test case, it will create the files as you show us on the CSV and move them to the new folders based on the folderName column.
The code will look for the files on the current directory, before testing it with the real files, Set-Location (cd) to that folder.
If you're not sure if the code will work you can add a -WhatIf switch to Rename-Item and Move-Item.
Note, I have removed ? from the newName column since it's an invalid character on Windows. See this answer for more details.
# Go to a temporary folder for testing
Set-Location path/to/temporaryfolder/here
# Here you would use:
# $csv = Import-Csv path/to/csv.csv
$csv = #'
folderName newName oldName
01 Course Overview 01_Course_Overview 1280x720.mp4
02 Introduction to PowerShell 01_Introduction to PowerShell 1280x720 (1).mp4
02 Introduction to PowerShell 02_Who Is This Course For 1280x720 (2).mp4
02 Introduction to PowerShell 03_What Is PowerShell 1280x720 (3).mp4
02 Introduction to PowerShell 04_Windows PowerShell and PowerShell 7 1280x720 (4).mp4
'# -replace ' +',',' | ConvertFrom-Csv
# Create test files, this part is only for testing the code
$csv.foreach({ New-Item $_.oldName -ItemType File })
foreach($line in $csv)
{
if(-not (Test-Path $line.folderName))
{
# Create the Folder if it does not exist
New-Item $line.folderName -ItemType Directory -Verbose
}
Rename-Item -LiteralPath $line.oldName -NewName $line.newName
Move-Item -LiteralPath $line.newName -Destination $line.folderName
}
If I understand correctly, your real CSV file contains folder and/or file names with characters that are invalid like the ?.
To fix that, you can choose to remove those characters from the CSV file first, OR make sure you remove them before creating a folder or renaming a file.
For both options, you can use this small helper function:
function Remove-InvalidNameChars {
param(
[Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[String]$Name,
[ValidateSet('File', 'Path')]
[string]$Type ='File'
)
if ($Type -eq 'File') {
$invalidChars = [IO.Path]::GetInvalidFileNameChars() -join ''
}
else {
$invalidChars = [IO.Path]::GetInvalidPathChars() -join ''
}
# build a regex string from the invalid characters
$removeThese = "[{0}]" -f [RegEx]::Escape($invalidChars)
# output the name with invalid characters removed
$Name -replace $removeThese
}
Method 1: remove the invalid characters from the CSV file and use cleaned-up data:
$sourcePath = 'D:\Test'
$csvFile = Join-Path -Path $sourcePath -ChildPath 'index.csv'
$csvData = Import-Csv -Path $csvFile -Delimiter ';'
foreach ($item in $csvData) {
$item.folderName = Remove-InvalidNameChars -Name $item.folderName -Type Path
$item.newName = Remove-InvalidNameChars -Name $item.newName -Type File
}
$csvData | Export-Csv -Path $csvFile -Delimiter ';' -Force # rewrite the CSV file if you like
# now use the cleaned-up data in $csvData for the rest of the code:
foreach ($item in $csvData) {
# create the output folder if this does not already exist
$targetPath = Join-Path -Path $sourcePath -ChildPath $item.folderName
$null = New-Item -Path $targetPath -ItemType Directory -Force
# move and rename the file if found
$sourceFile = Join-Path -Path $sourcePath -ChildPath $item.oldName
if (Test-Path -Path $sourceFile -PathType Leaf) {
$targetFile = Join-Path -Path $targetPath -ChildPath $item.newName
Move-Item -Path $sourceFile -Destination $targetFile
}
}
Method 2: leave the csv data as-is and make sure you remove invalid characters while renaming/moving:
$sourcePath = 'D:\Test'
$csvFile = Join-Path -Path $sourcePath -ChildPath 'index.csv'
$csvData = Import-Csv -Path $csvFile -Delimiter ';'
foreach ($item in $csvData) {
# create the output folder if this does not already exist
$targetPath = Join-Path -Path $sourcePath -ChildPath (Remove-InvalidNameChars -Name $item.folderName -Type Path)
$null = New-Item -Path $targetPath -ItemType Directory -Force
# move and rename the file if found
$sourceFile = Join-Path -Path $sourcePath -ChildPath (Remove-InvalidNameChars -Name $item.oldName -Type File)
if (Test-Path -Path $sourceFile -PathType Leaf) {
$targetFile = Join-Path -Path $targetPath -ChildPath (Remove-InvalidNameChars -Name $item.newName -Type File)
Move-Item -Path $sourceFile -Destination $targetFile
}
}
Note that Move-Item can move a file to a new destination and rename it at the same time, so you do not need Rename-Item
P.S. I noticed in your example CSV there are no extensions to the newName filenames..
If that is the case in real life, you need to add these aswell.
For that change the Move-Item line to:
Move-Item -Path $sourceFile -Destination ([IO.Path]::ChangeExtension($targetFile, [IO.Path]::GetExtension($sourceFile)))
I have the below script which is working fine for what we need thus far however running into an error as the file names have changed.
With the below script it is providing a Cannot Find Path error as the filenames have changed.
Where am i going wrong with this one?
## Add ShareFile PowerShell Snap-in
Add-PSSnapin ShareFile
## Create new authentiation file
#New-SfClient -Name "C:\Sharefile\SVCACC.sfps" -Account midl
## Variables ##
$OutputAppReqFID = "fo4a3b58-bdd6-44c8-ba11-763e211c183f"
$Project = 'M000'
$LocalPath = "\\file.server.au\$project\DATA\DATA CUSTODIAN\OUTPUT\"
$sfClient = Get-SfClient -Name C:\sharefile\SVCACC.sfps
$OutputAppReqFID_URL = (Send-SfRequest $sfClient -Entity Items -id $OutputAppReqFID).Url
## Create PS Drive ##
New-PSDrive -Name "sfDrive-$($project)" -PSProvider ShareFile -Client $sfClient -Root "\" -RootUri $OutputAppReqFID_URL
## Copy all files from folders to ShareFile
foreach ($file in Get-ChildItem -Path $LocalPath -Recurse -Force | Where-Object {$_.Mode -ne "d-----"} | Select FullName -ExpandProperty FullName) {
Get-ChildItem $file -Recurse | Rename-Item -NewName { $_.Directory.Name+'_'+$_.Name}
Copy-SfItem -Path $file -Destination "sfDrive-$($project):"
#remove-item $file
}
<## Remove all folders from UNC directory
foreach ($folder in Get-childitem -Path $LocalPath -Recurse | Where-Object {$_.Mode -eq "d-----"} | Select-Object -ExpandProperty FullName) {
remove-item $folder
}
#>
## Remove PS Drive ##
Remove-PSDrive "sfdrive-$($project)"
Error recieved is below:
Copy-SfItem : Cannot find path '\\file.server.au\M000\DATA\DATA CUSTODIAN\OUTPUT\New Text Document.txt' because it does not exist.
At line:43 char:6
+ Copy-SfItem -Path $file -Destination "sfDrive-$($project):"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (\\file.serve...xt Document.txt:String) [Copy-SfItem], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,ShareFile.Api.Powershell.CopySfItem
The problem is that you are renaming $file and then trying to Copy-SfItem -Path $file in the very next line. This reference to $file is still pointing to the old name before the rename. You need to instead capture the path to the newly renamed filepath and then reference that in your Copy-SfItem command
foreach ($file in Get-ChildItem -Path $LocalPath -Recurse -Force |
Where-Object { $_.Mode -ne 'd-----' } |
Select-Object -ExpandProperty FullName) {
# capture reference to the new file name by using -PassThru switch with
# the Rename-Item cmdlet and saving in a variable ($renamedFile)
$renamedFile = Get-ChildItem $file -Recurse |
Rename-Item -NewName { $_.Directory.Name + '_' + $_.Name } -PassThru
# Now we copy using our $newFile reference
Copy-SfItem -Path $renamedFile -Destination "sfDrive-$($project):"
# followed by removing the file if needed
# remove-item $renamedFile
}
This can be cleaned up some as well.
Adding the -File switch to Get-ChildItem will give us only files removing the need for | Where-Object {$_.Mode -ne "d-----"}
Taking off the | Select-Object -ExpandProperty FullName from the end of the foreach will eliminate the need to calling Get-ChildItem again in the first foreach loop.
foreach ($file in Get-ChildItem -Path $LocalPath -Recurse -Force -File ) {
# capture reference to the new file name by using -PassThru switch with
# the Rename-Item cmdlet and saving in a variable ($newFile)
$renamedFile = $file | Rename-Item -NewName { $_.Directory.Name + '_' + $_.Name } -PassThru
# Now we copy using our $newFile reference
Copy-SfItem -Path $renamedFile -Destination "sfDrive-$($project):"
# followed by removing the file if needed
# remove-item $renamedFile
}
Updates based on your comments
foreach ($file in Get-ChildItem -Path $LocalPath -Recurse -Force -File ) {
$file = if ($file.Directory.FullName -ne $LocalPath) {
# capture reference to the new file name by using -PassThru switch with
# the Rename-Item cmdlet and saving in a variable ($file)
$file | Rename-Item -NewName { $_.Directory.Name + '_' + $_.Name } -PassThru
}
else {
#Pass thru $file with no changes
$file
}
# Now we copy using our $file reference
Copy-SfItem -Path $file -Destination "sfDrive-$($project):"
# followed by removing the file if needed
# remove-item $file
}
I am trying to copy latest 30days files to the folder by this code:
$month = (get-date).AddDays(-30).ToString("yyyMM")
$lastmonthfiles = Write-Host (-join('DCP_', $month,"*.csv"))
Copy-Item -Path Write-Host (-join ("C:\DC+\History\", $lastmonthfiles)) -Destination C:\DC+\History\Backup
but I am having a problem in path in copy-item instruction, which is
Copy-Item : A positional parameter cannot be found that accepts argument 'C:\DC+\History\'.
At line:5 char:1
+ Copy-Item -Path Write-Host (-join ("C:\DC+\History\", $lastmonthfiles ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Copy-Item], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.CopyItemCommand
I am new in PowerShell, and programming so, I could not understand, how can I update and achieve a goal.
Try this:
$Source = Get-Item "C:\Games"
$Destination = "E:\Backup"
$LogFile = "C:\Copy-LogFile.txt"
Copy-Item -Path $( $Source | Where LastWriteTime -LE (Get-Date).AddDays(-30) ) -Destination $Destination -Recurse -PassThru |
Out-File $LogFile -Force
This takes care of your copy function and generates an output log.
Sorry, but your code makes no sense to me...
If it is your aim to copy files to a subfolder in a backup path, try this:
# get the date to use as subfolder to copy to and also to filter the last 30 days files
$refDate = (Get-Date).AddDays(-30).Date # .Date sets it to midnight
# set this variable to the folder where the csv are to be found
$sourceFolder = 'X:\Path\To\Where\The\Files\Are'
# create a destination path to copy to (just a string)
$destination = Join-Path -Path 'C:\DC+\History\Backup' -ChildPath ($refDate.ToString("yyyMM"))
# create this destination folder
$null = New-Item -Path $destination -ItemType Directory -Force
# get the files and copy them to the destination folder
Get-ChildItem -Path $sourceFolder -Filter '*.csv' -File | # filter on CSV fies only
Where-Object { $_.LastWriteTime -ge $refDate } | # filter on date 'last 30 days'
Copy-Item -Destination $destination -Force
I want to remove bak files after zip from all subfolder for given path, so all duplicate files with extension .bak will be removed,i run below script but getting error.
$filePath = "d:\Test\"
$Afiles = Get-ChildItem -Recurse -Path $filePath | Where-Object {$_.Extension -eq ".bak"}
$Bfiles = Get-ChildItem -Recurse -Path $filePath | Where-Object {$_.Extension -eq ".7z"}
$Alist = #()
$Blist = #()
foreach( $A in $Afiles) {
$Alist += $A.baseName
}
foreach( $B in $Bfiles) {
$Blist += $B.baseName
}
foreach($A in $Alist) {
if($Blist -contains $a)
{
rm ("$A.bak")
}
}
I am receiving below error :
Remove-Item : Cannot find path 'C:\Users\******\Desktop\master_backup_2015_08_21_013722_8370267.bak' because it does not exist.
At C:\Users\*****\Desktop\duplicatedelete1.ps1:26 char:10
+ rm <<<< ("$A.bak")
+ CategoryInfo : ObjectNotFound: (C:\Users\****....722_8370267.bak:String) [Remove-Item], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.RemoveItemCommand
You're receiving error because you're not specifying full path for rm command and PowerShell tries to delete file in your current directory.
Try this:
$Path = 'D:\Test'
Get-ChildItem -Path $Path -Recurse -Filter '*.7z' | ForEach-Object {
$BackupFile = Join-Path -Path (Split-Path $_.FullName -Parent) -ChildPath ($_.BaseName + '.bak')
if(Test-Path -Path $BackupFile){
Remove-Item -Path $BackupFile
}
}
I have this script where i want to move files from one path to another:
$logArchieveDirectory='C:\LogArchieve\'+$archiveTillDate.Day+$archiveTillDate.Month+$archiveTillDate.Year;
$sourcePathServer = 'C:\DDS\Server\LOGS';
$destPathServer=$logArchieveDirectory+'\Server';
#Create Directories
New-Item -ItemType directory -Path $logArchieveDirectory;
New-Item -ItemType directory -Path $destPathServer;
#Moving Logs to new temporary log archieve directories
foreach( $item in (Get-ChildItem $sourcePathServer | Where-Object { $_.CreationTime -le $archiveTillDate }) )
{
Move-Item $item $destPathServer -force;
}
However, i have specified both paths fine But i keep getting this error when i run this script.
Move-Item : Cannot find path 'C:\DDS\WorkFolder\WebAdapter' because it does not exist.
At C:\DDS\WorkFolder\powerShellScript08062014.ps1:38 char:11
+ Move-Item <<<< $item $destPathController;
+ CategoryInfo : ObjectNotFound: (C:\DDS\WorkFolder\WebAdapter:String) [Move-Item], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.MoveItemCommand
The C:\DDS\WorkFolder\ is actually the folder where my script file is located. But what i am not understanding is why it is looking for the folder here and not where the path is given i.e. $sourcePathServer?
Your script passes relative paths to Move-Item and it defaults to the working directory. An easy solution is to pass absolute paths using .FullName property:
foreach ($item in (Get-ChildItem $sourcePathServer `
| Where-Object { $_.CreationTime -le $archiveTillDate })) {
Move-Item $item.FullName $destPathServer -force
}
Also using foreach in conjunction with pipelines doesn't look very elegant because of all the parentheses. You can get rid of it:
Get-ChildItem $sourcePathServer `
| Where-Object { $_.CreationTime -le $archiveTillDate } | Foreach-Object {
Move-Item $_.FullName $destPathServer -Force
}