Powershell - Rename File and Upload - powershell

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
}

Related

copy newer files without keeping folders

I have a folder with a number of subfolders containing files and want to copy all files to the root folder but only overwrite if newer.
In powershell I can do -
Get-ChildItem D:\VaM\Custom\Atom\Person\Morphs\temp2\female -Recurse -file | Copy-Item -Destination D:\VaM\Custom\Atom\Person\Morphs\female
But this will overwrite all files, I only want to overwrite files if the copied file is newer.
robocopy can overwrite only older this but keeps the folder structure.
Try this
$root = 'D:\VaM\Custom\Atom\Person\Morphs\temp2\female'
[bool]$Delete = $false
Get-ChildItem $root -Recurse -File |
Where-Object {$_.DirectoryName -ne $root } | # Do not touch files already seated in root
ForEach-Object {
$rootNameBrother = Get-Item "$root\$($_.Name)" -ea 0
if($rootNameBrother -and $rootNameBrother.LastWriteTime -lt $_.LastWriteTime) {
# RootFile with same name exists and is Older - Copy and override
Copy-Item -Path $_.FullName -Destination $rootNameBrother.FullName -Force
}
elseif ($rootNameBrother -and $rootNameBrother.LastWriteTime -ge $_.LastWriteTime) {
# RootFile with same name exists and is Newer or same Age
# Delete non root File if allowed
if($Delete) { Remove-Item $_.FullName -Force }
}
}
Set...
$Delete = $true
...if you wish to delete non root files that could not be copied because there already was a file with the same name and greater modiefydate in root.
You also can set the
$VerbosePreference = "Continue"
$WhatIfPreference = "Continue"
variables, just to be safe when you execute the script for the first time.
If you wish to delete all empty subfolder, you can run this:
$allFolders =`
Get-ChildItem $root -Recurse -Directory |
ForEach-Object {
# Add now Depth Script Property
$_ | Add-Member -PassThru -Force -MemberType ScriptProperty -Name Depth -Value {
# Get Depth of folder by looping through each letter and counting the backshlashes
(0..($this.FullName.Length - 1) | ForEach {$this.FullName.Substring($_,1)} | Where-Object {$_ -eq "\"}).Count
}
}
# Sort all Folder by new Depth Property annd Loop throught
$allFolders | Sort -Property Depth -Descending |
ForEach-Object {
# if .GetFileSystemInfos() method return null, the folder is empty
if($_.GetFileSystemInfos().Count -eq 0) {
Remove-Item $_.FullName -Force # Remove Folder
}
}
You can do it like this:
$source = 'D:\VaM\Custom\Atom\Person\Morphs\temp2\female'
$destination = 'D:\VaM\Custom\Atom\Person\Morphs\female'
Get-ChildItem -Path $source -Recurse -File | ForEach-Object {
# try and get the existing file in the destination folder
$destFile = Get-Item -Path (Join-Path -Path $destination -ChildPath $_.Name) -ErrorAction SilentlyContinue
if (!$destFile -or $_.LastWriteTime -gt $destFile.LastWriteTime) {
# copy the file if it either did not exist in the destination or if this file is newer
Write-Host "Copying file $($_.Name)"
$_ | Copy-Item -Destination $destination -Force
}
}
I ended up doing this:
Get-ChildItem G:\VaM\Custom\Atom\Person\Morphs\temp2\ -Recurse |
Where-Object { $_.PSIsContainer -eq $true } |
Foreach-Object { robocopy $_.FullName G:\VaM\Custom\Atom\Person\Morphs\female /xo /ndl /np /mt /nfl}
it runs through the directory structure and copys the contents of each directory to the destination but only overwrites older files.

PowerShell works only when running in a console and not as a PS1 executable file

I am fairly new to PowerShell and am having challenges trying to get a PS1 executable file to work. Running the script in a PowerShell console works completely fine and copy's items and creates the correct log filename.
The expectation would be to Right-click the PS1 file containing the script, run the script with "Run with PowerShell", and then allow the script to finish with a log file populated and files copied when user prompt selects yes.
At this point, there are no errors messages, other than the PS1 file script gets replaced by ton of unrecognizable symbols/characters and creates the log file as "Box Project Files.ps1JobFileLocations.log" instead of "JobFileLocations.log".
The PowerShell version being used is 5.1. Windows 10 OS. Set-ExecutionPolicy was set to Unrestricted and confirmed as Unrestricted for CurrentUser and LocalMachine. Unblock-File was also tried.
Below is the script that works in a PowerShell Console but not as a PS1 executable file.
# Drawing Tag Searches
$MechDWGFilterList = #('*IFC*','*mech*', '*permit*', '*final*')
$DatabaseFilterList = #('*field*','*software*')
# Root folder and destination folder
$JobNumber = '*'+(Read-Host -Prompt 'Enter in job number')+'*'
$srcRoot = 'C:\Users\username\Box\'
$JobRoot = (Get-ChildItem -Path $srcRoot -Filter "*Active Projects*" -Recurse -Directory -Depth 1).Fullname
$dstRoot = $MyInvocation.MyCommand.Path
# Find job numer pdf file
$JobFolder = (Get-ChildItem -Path $JobRoot -Filter "$JobNumber" -Recurse -Directory -Depth 0).Fullname
$Logfile = $dstRoot+"JobFileLocations.log"
$reply = Read-Host -Prompt "Make a copy of relevant project files to local drive?[y/n]"
# Find sub-folder from job folder
$ProposalFolder = (Get-ChildItem -Path $JobFolder -Filter "*Proposals*" -Recurse -Directory).Fullname
$MechDWGFolder = (Get-ChildItem -Path $JobFolder -Filter "*Plans*" -Recurse -Directory).Fullname
$SubmittalFolder = (Get-ChildItem -Path $JobFolder -Filter "*Submittal*" -Recurse -Directory).Fullname
$DatabaseFolder = (Get-ChildItem -Path $JobFolder -Filter "*Backup*" -Recurse -Directory).Fullname
$EstimateFolder = (Get-ChildItem -Path $JobFolder -Filter "*Estimate*" -Recurse -Directory).Fullname
# Find files from list
$ProposalList = Get-ChildItem -Path $ProposalFolder -Filter '*proposal*.pdf' -r | Sort-Object -Descending -Property LastWriteTime | Select -First 1
$MechDWGList = Get-ChildItem -Path $MechDWGFolder -Filter *.pdf -r | Sort-Object -Descending -Property LastWriteTime
$SubmittalList = Get-ChildItem $SubmittalFolder -Filter '*submittal*.pdf' -r | Sort-Object -Descending -Property LastWriteTime | Select -First 1
$DatabaseList = Get-ChildItem $DatabaseFolder -Filter *.zip -r | Sort-Object -Descending -Property LastWriteTime | Select -First 1
$EstimateList = Get-ChildItem -Path $EstimateFolder -Filter *.xl* -r | Sort-Object -Descending -Property LastWriteTime
# Log file path location and copy file to local directory
# Function to add items to a log text file
Function LogWrite
{
Param ([string]$logstring)
Add-content $Logfile -value $logstring
}
# Log file path location and copy file to local directory
LogWrite "::==========================================::`n|| Project Document Paths ||`n::==========================================::"
LogWrite "`nNote: If a section has more than one file path, files are listed from most recent to oldest.`n"
LogWrite "----------Scope Document/Proposal(s)----------"
foreach ($file in $ProposalList)
{
LogWrite $file.FullName
if ( $reply -match "[yY]" )
{
Copy-Item -Path $($file.FullName) -Destination $dstRoot
}
}
LogWrite "`n-------------Mechanical Drawing(s)------------"
foreach ($file in $MechDWGList)
{
# Where the file name contains one of these filters
foreach($filter in $MechDWGFilterList)
{
if($file.Name -like $filter)
{
LogWrite $file.FullName
if ( $reply -match "[yY]" )
{
Copy-Item -Path $($file.FullName) -Destination $dstRoot
}
}
}
}
LogWrite "`n-------------Controls Submittal(s)------------"
foreach ($file in $SubmittalList)
{
LogWrite $file.FullName
if ( $reply -match "[yY]" )
{
Copy-Item -Path $($file.FullName) -Destination $dstRoot
}
}
LogWrite "`n-------------------Database-------------------"
foreach ($file in $DatabaseList)
{
LogWrite $file.FullName
if ( $reply -match "[yY]" )
{
Copy-Item -Path $($file.FullName) -Destination $dstRoot
}
}
LogWrite "`n------------------Estimate(s)-----------------"
foreach ($file in $EstimateList)
{
LogWrite $file.FullName
if ( $reply -match "[yY]" )
{
Copy-Item -Path $($file.FullName) -Destination $dstRoot
}
}
# If running in the console, wait for input before closing.
if ($Host.Name -eq "ConsoleHost")
{
Write-Host "Press any key to continue..."
$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyUp") > $null
}
Could someone help me understand what is wrong with running the script as a PS1 file?
The problem with your script is this particular line:
$dstRoot = $MyInvocation.MyCommand.Path
$MyInvocation.MyCommand.Path resolves the rooted filesystem path to the script itself - which is why you get Box Project Files.ps1 (presumably the name of the script) in the log path.
The get the path of the parent directory of any file path, you can use Split-Path -Parent:
$dstRoot = Split-Path -LiteralPath $MyInvocation.MyCommand.Path -Parent
That being said, since Windows PowerShell 3.0, both the directory and script file paths have been available via the $PSCommandPath and $PSScriptRoot automatic variables, so you can simplify the code to just:
$dstRoot = $PSScriptRoot

Powershell add text to start, end of output file

I have a folder containing multiple text files that I am combining into one file. On that output file I need to add a string to the top and the bottom of the file. I have tried using insert and I keep getting an error. The file is contained in the $Output variable
My code so far:
if(!(Test-Path -Path $PathDump)) {
# create the folder if it does not yet exist
New-Item -ItemType Directory $PathDump
}
# move all *.txt items from 'C:\RemoveFirst\txt' to 'C:\RemoveFirst\DumpARoo'
# EXCEPT the output file itself
$Path = (Get-ChildItem -Path $Path -Filter '*.txt' -File).FullName | Where-Object { $_ -ne $Output}
Move-Item -Path $Path -Destination $PathDump # move (not copy) files into new directory to concat
Get-ChildItem -Path $PathDump -Filter '*.txt' -File | ForEach-Object {
'' # Output an empty line at SOF
'---------------------------------------------------------------'
$_ | Get-Content | Select-Object -Skip 1 | Select-Object -SkipLast 1
'---------------------------------------------------------------'
'' # Output an empty line at EOF
} | Add-Content -Path $OutPut
The story continues ;)
Try this:
$Path = 'C:\RemoveFirst\*.txt'
$PathDump = 'C:\RemoveFirst\DumpARoo'
$Output = 'C:\RemoveFirst\TestingFile.txt'
if(!(Test-Path -Path $PathDump)) {
# create the folder if it does not yet exist
New-Item -ItemType Directory $PathDump
}
# move all *.txt items from 'C:\RemoveFirst\txt' to 'C:\RemoveFirst\DumpARoo'
# EXCEPT the output file itself
$Path = (Get-ChildItem -Path $Path -Filter '*.txt' -File).FullName | Where-Object { $_ -ne $Output}
Move-Item -Path $Path -Destination $PathDump # move (not copy) files into new directory to concat
# Output 'SOF'
"SOF" | Add-Content -Path $OutPut
Get-ChildItem -Path $PathDump -Filter '*.txt' -File | ForEach-Object {
# output the content of the current file
$_ | Get-Content | Select-Object -Skip 1 | Select-Object -SkipLast 1 | Add-Content -Path $OutPut
}
# Output 'EOF'
"EOF" | Add-Content -Path $OutPut

Duplicate file name as folder, insert file

I am trying to use Powershell to
scan folder D://Mediafolder for names of media files
create a folder for each media file scanned, with same name
insert each media file in to matching folder name.
I can find no documentation or thread of this, and I am more fluent in Linux than Windows. I've tried many times to piece this together, but to no avail.
Hope this will help :)
This will create a folder for each file with the same name, so if you have a file called xyz.txt, it will create a folder called xyz and move the file to this folder.
$path = "D:\MediaFolder"
$items = Get-ChildItem $path
Foreach ($item in $items)
{
$folderName = $item.name.Split('.')[0]
New-Item "$path\$folderName" -ItemType Directory
Move-Item -Path "$path\$item" -Destination "$path\$foldername"
}
File Sorting based on extension should do the job:
$folder_path = read-host "Enter the folder path without space"
$file = gci $folder_path -Recurse | ? {-not $_.psiscontainer}
$file | group -property extension | % {if(!(test-path(join-path $folder_path -child $_.name.replace('.','')))){new-item -type directory $(join-path $folder_path -child $_.name.replace('.','')).toupper()}}
$file | % { move-item $_.fullname -destination $(join-path $folder_path -child $_.extension.replace(".",""))}
$a = Get-ChildItem $folder_path -recurse | Where-Object {$_.PSIsContainer -eq $True}
$a | Where-Object {$_.GetFiles().Count -eq 0} | Remove-Item -Force
This will iterate over the files in the media_dir and move those with the extensions in media_types to a folder with the same basename. When you are satisfied that the files will be moved to the correct directory, remove the -WhatIf from the Move-Item statement.
PS C:\src\t> type .\ms.ps1
$media_dir = 'C:\src\t\media'
$new_dir = 'C:\src\t\newmedia'
$media_types = #('.mp3', '.mp4', '.jpeg')
Get-ChildItem -Path $media_dir |
ForEach-Object {
$base_name = $_.BaseName
if ($media_types -contains $_.Extension) {
if (-not (Test-Path $new_dir\$base_name)) {
New-Item -Path $new_dir\$base_name -ItemType Directory | Out-Null
}
Move-Item $_.FullName $new_dir\$base_name -WhatIf
}
}

Rename Folders After Copy

I'm having problems after a folder is copied to a different location, I need to rename the folders in the directory to remove ".deploy" from the end, but I get the following error below. I have Googled around for PowerShell admin permissions, but cannot seem to find a 'catch-all' for my scenario.
Get-Content : Access to the path 'C:\OldUserBackup\a.deploy' is denied.
At C:\PSScripts\DesktopSwap\TestMergeDir.ps1:28 char:14
+ (Get-Content $file.PSPath) |
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (C:\OldUserBackup\a.deploy:String) [Get-Content], UnauthorizedAccessException
+ FullyQualifiedErrorId : GetContentReaderUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetContentCommand
Here is what I have:
$UserName = [Environment]::UserName
$CurrUser = [Environment]::UserName + '.deploy'
$OldUserDir = 'C:\OldUserBackup'
$CurrDate = Get-Date -format G
$PathExist = Test-Path $OldUserDir
if ($PathExist -eq $true) {
#Copy Desktop, Downloads, Favorites, Documents, Music, Pictures, Videos
Copy-Item -Path $OldUserDir -Destination C:\Users\$UserName\Desktop\CopyTest -Recurse -Force
$configFiles = Get-ChildItem $OldUserDir *.deploy -rec
foreach ($file in $configFiles) {
(Get-Content $file.PSPath) |
Foreach-Object { $_ -replace ".deploy", "" } |
Set-Content $file.PSPath
}
}
You should use the -Directory switch on the Get-ChildItem cmdlet to only get directories. Then use the Rename-Item cmdlet to rename the folders. I use the -replace function with a simple regex to get the new folder name:
$deployFolders = Get-ChildItem $OldUserDir *.deploy -rec -Directory
$deployFolders | Foreach {
$_ | Rename-Item -NewName ($_.Name -replace ('\.deploy$') )
}
You don't even have to use the Foreach-Object cmdlet (Thanks to AnsgarWiechers):
Get-ChildItem $OldUserDir *.deploy -rec -Directory |
Rename-Item -NewName { $_.Name -replace ('\.deploy$') }