PowerShell Move-Item $filename - powershell

I searched, i googled.. about to smash my head on the table
how come this will not work?
move-Item $path$file $targetdir
it gives me an error
Move-Item : An object at the specified path C:\Repository\test.csv
does not exist.
now if i debug this and i output using
write-output move-Item $path$file $targetdir
and take that output and paste it (file name with path and destination) it works!
and trust me the file is there. =\
Code below
$path = 'C:\test\'
$TimeStamp = Get-Date -Format "MM-dd-yyyy_hh-mm-ss"
$LogFile = Get-Date -Format "MM_dd_yyyy"
$targetdir = "C:\test\Uploaded\"
#Get-ChildItem -path $path\* -Include *.csv | foreach-object {$_.Fullname} | Format-Table name -hidetableheaders | Out-File $path\list.txt
Get-ChildItem -path $path\* -Include *.csv | Format-Table name -hidetableheaders | Out-File $path\list2.txt
get-content C:\test\list2.txt | where {$_ -ne ""} | out-file C:\test\list.txt
Remove-Item C:\test\list2.txt
$list = get-content C:\test\list.txt
foreach ($file in $list)
{
$ftp = "ftp://REMOVED/$file"
"ftp url: $ftp"
$webclient = New-Object System.Net.WebClient
$uri = New-Object System.Uri($ftp)
"Uploading $file..."
$succeeded = $true;
& {
trap { $script:succeeded = $false; continue }
$webclient.UploadFile($uri, $path+$file)
}
if ($succeeded)
{
echo $file 'Was successfully uploaded!' $Timestamp >> logfile$LogFile.log
move-Item -path $path$file -destination $targetdir
#test-path $path$file
}
else
{
echo $file 'Was not successfully uploaded, will retry later' $Timestamp >> logfile$LogFile.log
}
}
exit

Basics are:
Test-Path before you move it (file and destination)
Move the file, ensure you have permission (force it to move)
so:
echo $targetdir
echo "$path$file"
if (!(Test-Path $targetdir)) {
New-Item -ItemType directory $targetdir
}
if(Test-Path "$path$file") {
Move-Item "$path$file" $targetdir -Force
} else {
echo "file does not exist"
}
If you loop over a collection you have to use the ".FullName" property of the object:
Get-ChildItem $path | ForEach-Object { Move-Item $_.FullName $targetdir -Force }

Does the target directory already exist? I believe Move-Item will fail if the target directory doesn't exist. If that's the case, you can simply test for existence of the directory beforehand and then create as necessary.
If (!(Test-Path -Path $targetdir)) {
New-Item -ItemType directory -Path $targetdir
}

This worked for me. Thank you #TheMadTechnician. Hopes this helps everyone
$TimeStamp = Get-Date -Format "MM-dd-yyyy_hh-mm-ss"
$LogFile = Get-Date -Format "MM_dd_yyyy"
$path='C:\test\'
$targetDir = 'C:\test\Uploaded\'
$fileList = Get-ChildItem $path*.csv
If(!(Test-Path $TargetDir)){New-Item -ItemType Directory -Path $TargetDir|Out-Null}
$fileList | Select -ExpandProperty Name | Out-File 'C:\test\list.txt'
$list = get-content C:\test\list.txt
foreach ($file in $list)
{
$ftp = "ftp://REMOVED/$file"
"ftp url: $ftp"
$webclient = New-Object System.Net.WebClient
$uri = New-Object System.Uri($ftp)
"Uploading $file..."
$succeeded = $true;
& {
trap { $script:succeeded = $false; continue }
$webclient.UploadFile($uri, $path+$file)
}
if ($succeeded)
{
echo $file 'Was successfully uploaded!' $Timestamp >> logfile$LogFile.log
move-Item -path $path$file -destination $targetdir$Timestamp"_"$file
#test-path $path$file
}
else
{
echo $file 'Was not successfully uploaded, will retry later' $Timestamp >> logfile$LogFile.log
}
}
exit

How about this then:
ForEach($File in $List){
Join-Path $path $file | Move-Item -Dest $Targetdir
}
Edit: Also... your creation of list.txt, it bothered me so I had to comment. Format-Table should be used for formatting text, not for selecting a value to output to a file. There's a better way to do that, consider this alternative:
Get-ChildItem "$path*.csv" | Select -ExpandProperty Name | Out-File $pathlist.txt
Since you say that $path = 'C:\test\' you are adding extra backslashes in there that may cause issues for some commands.
Edit2: Ok, if that doesn't work, why not work with the files themselves instead of outputting to a file, importing from that file, and then working with things.
$path='c:\test\'
$TargetDir = 'c:\test\NewDir'
$FileList = Get-ChildItem $path*.csv
If(!(Test-Path $TargetDir)){New-Item -ItemType Directory -Path $TargetDir|Out-Null}
$FileList | Move-Item -Destination $TargetDir
Then if you really want a list of those file names just pipe $FileList to Select and then to Out-File
$FileList | Select -ExpandProperty Name | Out-File 'C:\Test\list.txt'
Here, look through this and see if there's anything you like. I made a few changes, such as declaring paths at the beginning for everything, I moved the WebClient object creation outside of the loop, and changed how things are displayed on screen. Plus I skip the entire exporting to text file and re-importing it.
$path = 'C:\test'
$ftpaddr = 'ftp://ftp.example.com/uploads'
$TimeStamp = Get-Date -Format "MM/dd/yyyy hh:mm:ss tt"
$LogFile = Get-Date -Format "MM_dd_yyyy"
$LogDir = "C:\Test\Logs"
If(!(test-path $LogDir)){New-Item -ItemType Directory -Path $LogDir | Out-Null}
$targetdir = 'C:\test\Uploaded'
If(!(test-path $targetdir)){New-Item -ItemType Directory -Path $targetdir | Out-Null}
$list = Get-ChildItem -path $path\* -Include *.csv
$webclient = New-Object System.Net.WebClient
"ftp url: $ftpaddr"
foreach ($file in ($list|select -ExpandProperty Name))
{
$uri = New-Object System.Uri(("$ftpaddr/$file"))
Write-Host "Uploading $file... " -NoNewline -ForegroundColor White
$succeeded = $true
& {
trap { $script:succeeded = $false; continue }
$webclient.UploadFile($uri, "$Path\$file")
}
if ($succeeded)
{
Write-Host "Success!" -ForegroundColor Green
"$Timestamp`t$File was successfully uploaded!" | Out-File "$logdir\logfile$LogFile.log" -Append
move-Item -path "$path\$file" -destination $targetdir
}
else
{
Write-Host "Failed! Will retry later." -ForegroundColor Red
"$Timestamp`t$File was not successfully uploaded, will retry later" | Out-File "$logdir\logfile$LogFile.log" -Append
}
}

Related

Powershell - ForEach loop running twice on the same path instead of moving on next one

I am looking to move files from various paths to E:\backups\folder1\export, E:\Backups\folder2\backups, E:\backups\folder3\export and using below code...but strangely it runs twice on the same path and doesn't move to next path and duplicates the output too.
Cls
$date = Get-Date -Format "yyyMMdd_hhmmss"
#$sourcePath = "C:\Program Files\Atlassian\Application Data\JIRA\export\"
#$destPath = "E:\Backup\Jira\export"
$config = Import-Csv -Path 'E:\Backup\Scripts\Atlassian_Backups.csv'
#Write-Output $config
Start-Transcript -Path E:\Backup\Logs\Atlassian_backupMove.log
foreach ($item in $config)
{
Write-Host "Moving all files in '$($sourcePath)' to '$($destPath)'"
$fileList = #(Get-ChildItem -Path "$($sourcePath)" -File)
#Write-Output $fileList
if ($filelist.count -gt 0)
{
Write-host $filelist.Count
ForEach($file in $fileList)
{
try {
#Move-Item -Path $file.FullName -Destination ((Split-Path
$file.FullName).Replace("$($sourcePath)",$destPath)) -Force -ErrorAction Stop
Copy-Item -Path $file.FullName -Destination $destPath -Verbose -Force -ErrorAction Stop |
Format-table
}
catch{
Write-Warning "Unable to move '$($file.FullName)' to '$(((Split-Path
$file.FullName).Replace("$($sourcePath)",$destPath)))': $($_)"
return
}
}
}
}
Stop-Transcript
Rename-Item E:\Backup\Logs\Atlassian_backupMove.log
E:\Backup\Logs\Atlassian_backupMove_$date.log
write-host Log File has been created and renamed to Atlassian_backupMove_$date.log'
below was changed in my code and worked well.
{#Move-Item -Path $file.FullName -Destination ((Split-Path
$file.FullName).Replace("$($sourcePath)",$destPath)) -Force -ErrorAction Stop
Copy-Item -Path $file.FullName -Destination $item.destPath -Verbose -Force -
ErrorAction Stop | Format-table } catch{ Write-Warning "Unable to move
'$($file.FullName)' to '$(((Split-Path
$file.FullName).Replace("$($item.sourcePath)",$item.destPath)))': $($_)" return
}

How to log copied items during the backup script?

I need to make basic / or more advanced backup script that would copy items from folder A to folder B and then log what it did.
This copies the files just fine:
$source = 'path\gamybinis\*'
$dest = 'path\backup'
Get-ChildItem -Path $source -Recurse | Where-Object { $_.LastWriteTime -gt [datetime]::Now.AddMinutes(-5)
}| Copy-Item -Destination $dest -Recurse -Force
Write-Host "Backup started"
Pause
But after this I can't write the log with | Out-File, So I've tried this:
$source = 'path\gamybinis\*'
$dest = 'path\backup'
$logFile = 'path\log.txt'
$items = Get-ChildItem -Path $source -Recurse | Where-Object { $_.LastWriteTime -gt [datetime]::Now.AddMinutes(-5)
}
foreach($item in $items){
Out-File -FilePath $logFile -Append
Copy-Item -Path "$source\$item" -Destination $dest -Recurse -Force
}
Write-Host "Backup started"
Pause
This one does absolutely nothing, what exactly am I doing wrong?
(Advanced script part would be: backing up recently modified files then files should be archived to .rar/.zip, log file have to have structure that is easily readable and log file should have information which user was working on the device during the backup) - For those who are wondering.
If you can't use robocopy, in pure PowerShell code you could do this
$source = 'path\gamybinis' # no need for '\*' because you're specifying -Recurse
$dest = 'path\backup'
$logFile = 'path\log.txt'
# test if the destination path exists. If not, create it first
if (!(Test-Path -Path $dest -PathType Container)) {
$null = New-Item -Path $dest -ItemType Directory
}
Write-Host "Backup started"
Get-ChildItem -Path $source -Recurse |
Where-Object { $_.LastWriteTime -gt (Get-Date).AddMinutes(-5) } |
ForEach-Object {
$_ | Copy-Item -Destination $dest -Recurse -Force
Add-Content -Path $logFile -Value "$((Get-Date).ToString("yyyy-MM-dd HH:mm:ss")) - Copied file '$($_.FullName)'"
}
Write-Host "Backup done"
From your comments, I understand you have problems when using the -Container switch.
Below code does not use that and creates the folder structure of the copied files in the backup folder, strictly using Powershell code:
$source = 'path\gamybinis' # no need for '\*' because you're specifying -Recurse
$dest = 'path\backup'
$logFile = 'path\log.txt'
Write-Host "Backup started"
Get-ChildItem -Path $source -File -Recurse |
Where-Object { $_.LastWriteTime -gt (Get-Date).AddMinutes(-5) } |
ForEach-Object {
$target = Join-Path -Path $dest -ChildPath $_.DirectoryName.Substring($source.Length)
if (!(Test-Path $target -PathType Container)) {
# create the folder if it does not already exist
$null = New-Item -Path $target -ItemType Directory
}
$_ | Copy-Item -Destination $target -Force
Add-Content -Path $logFile -Value "$((Get-Date).ToString("yyyy-MM-dd HH:mm:ss")) - Copied file '$($_.FullName)'"
}
Write-Host "Backup done"

Powershell loop is only running once file per filename, even if the filename exists with multiple extensions

I'll be the first to admit that PowerShell isn't my strong suit, but I've pieced together the following after an evening of digging around on the internet. The end goal is to organize a huge drive of images by the DateTaken, as well as sidecar XMP files if they exist. It's probably not the most elegant code, but it almost works.
The last issue, which I can't figure out, is that the foreach loop only executes once per filename, regardless of extension. For example, only DSC00001.arw, DSC00001.xmp, or DSC00001.jpg would be processed.
Any points in the right direction would be appreciated.
Thanks!
$Folders = (Get-ChildItem -Path F:\ -Recurse -Directory -Force).FullName
$objShell = New-Object -ComObject Shell.Application
$Folders | % {
$objFolder = $objShell.namespace($_)
foreach ($File in $objFolder.items()) {
if (($File | Split-Path -Extension) -in ".arw",".gif",".tiff",".jpg",".png",".nef") {
Write-Host $File.Name`t -NoNewline -ForegroundColor Green
try {
$DateTaken = ($objFolder.getDetailsOf($File,12) -replace [char]8206) -replace [char]8207
$DateTaken = [DateTime]::ParseExact($DateTaken, "g", $null)
$Year = $DateTaken.ToString('yyyy')
$Date = $DateTaken.ToString('yyyy-MM-dd')
Write-Host $Date`t -ForegroundColor Blue -NoNewline
}
catch {
$Year = 'Other'
$Date = 'Other'
Write-Host $Date`t -ForegroundColor DarkRed -NoNewline
}
finally {
$DatePath = (Join-Path (Join-Path F:\ $Year ) $Date)
}
write-Host $File.Path -> (Join-Path $DatePath ($File | Split-Path -Leaf))-NoNewline
#Move-Item $File.Path (Join-Path $DatePath ($File | Split-Path -Leaf))
Write-Host Completed`n -NoNewline
if (Test-Path (Join-Path ($File | Split-Path) (($File | Split-Path -LeafBase) + '.xmp'))) {
Write-Host XMP:`t -ForegroundColor Magenta -NoNewLine
Write-Host (Join-Path ($File | Split-Path) (($File | Split-Path -LeafBase) + '.xmp')) -> (Join-Path ($DatePath) (($File | Split-Path -LeafBase) + '.xmp'))
#Move-Item (Join-Path ($File | Split-Path) (($File | Split-Path -LeafBase) + '.xmp')) (Join-Path ($DatePath) (($File | Split-Path -LeafBase) + '.xmp'))
}
}else {
Write-Host $File.Name is not an image `n -NoNewline -ForegroundColor DarkYellow
}
}
}
I'm not sure why $objFolder.items() doesn't actually return all expected files, but I would suggest using PowerShell's built-in file system provider to discover the actual files in each folder and then use $objFolder.ParseName() to obtain a reference you can pass to GetDetailsOf():
$Folders = (Get-ChildItem -Path F:\ -Recurse -Directory -Force).FullName
$objShell = New-Object -ComObject Shell.Application
$Folders | % {
$objFolder = $objShell.NameSpace($_)
foreach ($FileInfo in Get-ChildItem -LiteralPath $_ -File -Force) { # <-- trick is right here, use Get-ChildItem to discover the files in the folder
if($FileInfo.Extension -in ".arw",".gif",".tiff",".jpg",".png",".nef"){
$File = $objFolder.ParseName($FileInfo.Name) # <-- here we resolve the corresponding file item with shell app
# ... resolve and parse dateTaken here, just like before
# *.xmp file path might be easier to construct like this
$xmpPath = $FileInfo.FullName -replace "$($FileInfo.Extension)`$",'.xmp'
if(Test-Path -LiteralPath $xmpPath){
# ...
}
}
}
}

File renaming with powershell

Script is working almost how it is intended, still struggling with renaming duplicate files. I cannot figure out how to get it to name the files like
filename(1).ext
filename(2).ext
the closest I have gotten was
filename(1).ext
filename(1)(2).ext
#region actual script
$srcRoot = "C:\srcLocation"
$dstRoot = "C:\dstLocation"
$fileList = Get-ChildItem -Path $srcRoot -File -Force -Recurse
foreach ($file in $fileList) {
$fileName = $file.Name.ToUpper()
$fileExt = $file.Extension.ToUpper()
$dstFileName = $null
switch -Regex ($fileName)
{
'[A-Z]{4}-[0-9]{3}' { $dstFileName = $fileName }
'[A-Z]{4} [0-9]{3}' { $dstFileName = $fileName -replace '([A-Z]{4})\s([0-
9]{3})','$1-$2' }
'[A-Z]{4}[0-9]{3}' { $dstFileName = $fileName -replace '([A-Z]{4})([0-9]
{3})','$1-$2'}
Default { Write-Warning -Message "$fileName is not an expected filename"
}
}
if ($dstFileName) {
$dstDir = $dstFileName.Split('.')[0].Substring(0,8)
$dstPath = Join-Path -Path $dstRoot -ChildPath $dstDir
if (-not (Test-Path -Path $dstPath)) {
New-Item -Path $dstPath -ItemType Directory
}
$i = 1
if (test-path $dstPath\$dstFileName){
$dstFileName = $dstFileName.Split('.')[0] + "($i)" + $fileExt
While (test-path $dstPath\$dstFileName){
$i +=1
$dstFileName = $dstFileName -replace
}
}
Write-Verbose "Moving $($file.FullName)"
Move-Item -Path $($file.FullName) -Destination $dstPath\$dstFileName -
ErrorAction Continue
}
}
#endregion
You can simply use the Replace method of string objects in PowerShell. To verify your input, you can use a RegEx. Move-Item will throw an error, if the file already exists in the destination anyways. The complete script would look like this.
#region setup
New-Item -Path C:\srcpath,C:\dstpath -ItemType Directory
Set-Location C:\srcpath
New-Item 'ABCD123.txt','ABCD 123.txt','AbCD-123.txt','AAAA111.txt','BBBB 222.jpg','BBBB-222.txt' -ItemType File
#endregion
#region actual script
$srcRoot = "C:\srcpath"
$dstRoot = "C:\dstpath"
$fileList = Get-ChildItem -Path $srcRoot -File -Force -Recurse
foreach ($file in $fileList) {
$fileName = $file.Name.ToUpper()
$dstFileName = $null
switch -Regex ($fileName)
{
'[A-Z]{4}-[0-9]{3}' { $dstFileName = $fileName }
'[A-Z]{4} [0-9]{3}' { $dstFileName = $fileName -replace '([A-Z]{4})\s([0-9]{3})','$1-$2' }
'[A-Z]{4}[0-9]{3}' { $dstFileName = $fileName -replace '([A-Z]{4})([0-9]{3})','$1-$2'}
Default { Write-Warning -Message "$fileName is not an expected filename" }
}
if ($dstFileName) {
$dstDir = $dstFileName.Split('.')[0]
$dstPath = Join-Path -Path $dstRoot -ChildPath $dstDir
if (-not (Test-Path -Path $dstPath)) {
New-Item -Path $dstPath -ItemType Directory
}
Write-Verbose "Moving $($file.FullName)"
Move-Item -Path $($file.FullName) -Destination $dstPath\$dstFileName -ErrorAction Continue
}
}
#endregion
#region result
Write-Host '----- Result -----' -BackgroundColor DarkYellow
Get-ChildItem C:\dstpath -Recurse | Select-Object -ExpandProperty FullName
#endregion
Gets the Files in Source folder (Get-ChildItems)
Renames The File to include a - instead of a " " (Rename-Item)
Sets the Child Name property to the new name ($File.Name)
Creates new Folder in source based on first 4 Chars
Moves-Item to new created folder (move-item)
$Source = "C:\Start"
$Destination = "C:\End"
foreach($File in (Get-ChildItem -Path $Source -File -Recurse)){
Rename-Item $File.Fullname ($File.Name -replace " ", "-")
$file.Name = ($File.Name -replace " ", "-")
New-Item "$($Destination)\$($File.Name.Substring(0,3))" -ItemType directory
move-item $File.FullName -force -destination $Destination\$($File.Name.Substring(0,3))
}

creating a log file in powershell

I have the following powershell code in which,
backup of (original) files in folder1 is taken in folder2 and the files in folder1 are updated with folder3 files.
The concept of hotfix !!
cls
[xml] $XML = Get-content -Path <path of xml>
$main = $XML.File[0].Path.key
$hotfix = $XML.File[1].Path.key
$backup = $XML.File[2].Path.key
Get-ChildItem -Path $main | Where-Object {
Test-Path (Join-Path $hotfix $_.Name)
} | ForEach-Object {
Copy-Item $_.FullName -Destination $backup -Recurse -Container
}
write-host "`nBack up done !"
Get-ChildItem -Path $hotfix | ForEach-Object {Copy-Item $_.FullName -Destination $main -force}
write-host "`nFiles replaced !"
Now, as the backup of files is taken in folder2, I need to create a log file which contains - name of the file whose backup is taken, date and time, location where the backup is taken
Can anyone please help me with this?
I did the following code, but its of no use, as I cannot sync the both.
cls
$path = "C:\Log\Nlog.log"
$numberLines = 25
For ($i=0;$i -le $numberLines;$i++)
{
$SampleString = "Added sample {0} at {1}" -f $i,(Get-Date).ToString("h:m:s")
add-content -Path $path -Value $SampleString -Force
}
Any help or a different approach is appreciated !!
You can use the -PassThru switch parameter to have Copy-Item return the new items it just copied - then do the logging immediately after that, inside the ForEach-Object scriptblock:
| ForEach-Object {
$BackupFiles = Copy-Item $_.FullName -Destination $backup -Recurse -Container -PassThru
$BackupFiles |ForEach-Object {
$LogMessage = "[{0:dd-MM-yyyy hh:mm:ss.fff}]File copied: {1}" -f $(Get-Date),$_.FullName
$LogMessage | Out-File ".\backups.log" -Append
}
}