I added in New-Item command but it's not liking my syntax. Please help:
$Dir = get-childitem -path "\\ahs-bind01\ftptest01\CRAR" -recurse
$Source = $Dir | where {$_.extension -eq ".txt"}
#Declare the file path and sheet name
$file = "C:\Users\us6205\Desktop\DatabaseNameConfigs\Test\CareMoveFileParentDestPaths.xlsx"
$sheetName = "sheet1"
#Create an instance of Excel.Application and Open Excel file
$objExcel = New-Object -ComObject Excel.Application
$workbook = $objExcel.Workbooks.Open($file)
$sheet = $workbook.Worksheets.Item($sheetName)
$objExcel.Visible=$false
#Count max row
$rowMax = ($sheet.UsedRange.Rows).count
#Declare the starting positions
$rowLOC,$colLOC = 1,1
$rowUNC,$colUNC = 1,2
#loop to get values and store it
for ($i=1; $i -le $rowMax-1; $i++)
{
$LOC = $sheet.Cells.Item($rowLOC+$i,$colLOC).text
$UNC = $sheet.Cells.Item($rowUNC+$i,$colUNC).text
$Path = Get-ChildItem $UNC -recurse |
Where-Object { ($_.PSIsContainer -eq $true) -and ( $_.Name -match "$Source") }
$Dest = $Path | New-Item -path $Dest -itemtype directory | Move-Item -Path $Source -Destination $Dest -Force
}
#close excel file
$objExcel.quit()
Excel Doc:
Col A Col B
Test C:\Users\US6205\Desktop\Test\Unprocessed
Test1 C:\Users\US6205\Desktop\Test1\Unprocessed
The use of pipeline is not correct in below line:
$Dest = $Path | New-Item -path $Dest -itemtype directory | Move-Item -Path $Source -Destination $Dest -Force
This does not assign $Path to $Dest. It passes the $Path variable to next command on the right of the pipeline. $Dest is empty when you are trying to use in New-Item command. Instead, use in separate lines as below:
$Dest = $Path
New-Item -path $Dest -itemtype directory
Move-Item -Path $Source -Destination $Dest -Force
I would also recommend to use Test-Path cmdlet to check that the paths exist in your script.
Related
I am trying to build a script that can do the following
When I open the powershell script
it should ask me for a path where the files are located e.g.
C:\SearchPath\DateOfToday
Daily there will be a new folder e.g. 20221214.
Maybe it is possible to specify the path and the script will get today's date by itself or maybe just opening a windows explorer so I can select the SourceFolder.
A file must be copied e.g. Test.xmr and then renamed to Test.$xmr
some more files should be collected like
jpl, eml, html, pdf, cml, xmr, $xmr and xml
the collected files should be moved to the folder
C:\AnotherFolder\
What I have so far is this
$SourceFolder = "\\Path1\Dateoftoday"
$DestFolder = '\\Path2' #Path to Destination Folder
[array]$FileList = gci -Path $SourceFolder -Recurse -File -Filter "ABC.xmr"
[array]$FileList = gci -Path $SourceFolder -Recurse -File -Filter "ABC.jpl"
[array]$FileList = gci -Path $SourceFolder -Recurse -File -Filter "ABC.eml"
[array]$FileList = gci -Path $SourceFolder -Recurse -File -Filter "ABC.html"
[array]$FileList = gci -Path $SourceFolder -Recurse -File -Filter "ABC.pdf"
[array]$FileList = gci -Path $SourceFolder -Recurse -File -Filter "ABC.cml"
[array]$FileList = gci -Path $SourceFolder -Recurse -File -Filter "ABC.xml"
ForEach ($F in $FileList) {
Copy-Item $F.FullName (Join-Path $DestFolder ($F.Name))
}
I can specify the path and the filenames and then run it.
Sorry if it's basic, but I'm new at powershell.
Looks like you just need this:
$today = '{0:yyyyMMdd}' -f (Get-Date)
$SourceFolder = "\\Path1\$today"
$DestFolder = '\\Path2' #Path to Destination Folder
$filePattern = '*.xmr', '*.jpl', '*.eml', '*.html', '*.pdf', '*.cml', '*.xml', '*.$xmr'
# unclear about your question 2)..
# do we need to copy a file to the $SourceFolder first ??
# Copy-Item -Path 'X:\Somewhere\Test.xmr' -Destination (Join-Path -Path $SourceFolder -ChildPath 'Test.$xmr')
# copy all chosen files to the destination folder
Get-ChildItem -LiteralPath $SourceFolder -Recurse -File -Include $filePattern | ForEach-Object {
$_ | Copy-Item -Destination $DestFolder
}
As per your comment, hopefully this is what you need:
$today = '{0:yyyyMMdd}' -f (Get-Date)
$SourceFolder = "\\Path1\$today"
$DestFolder = '\\Path2' #Path to Destination Folder
$extensions = 'xmr', 'jpl', 'eml', 'html', 'pdf', 'cml', 'xml'
# ask for the filename
$baseName = Read-Host 'Please enter the file name without extension'
if (![string]::IsNullOrWhiteSpace($baseName)) {
# construct the filenames to copy
$pattern = $extensions | ForEach-Object { '{0}.{1}' -f $baseName.Trim(), $_ }
# copy all chosen files to the destination folder
Get-ChildItem -LiteralPath $SourceFolder -Recurse -File |
Where-Object { $pattern -contains $_.Name } | ForEach-Object {
# rename the extensions by prefixing it with a dollar sign ($)
$targetFile = Join-Path -Path $DestFolder -ChildPath ('{0}.${1}' -f $_.BaseName, $_.Extension.TrimStart("."))
$_ | Copy-Item -Destination $targetFile
# if you want ONLY the .xmr file to be renamed with as .$xmr use this instead:
# if ($_.Extension -eq '.xmr') {
# $targetFile = Join-Path -Path $DestFolder -ChildPath ('{0}.${1}' -f $_.BaseName, $_.Extension.TrimStart("."))
# $_ | Copy-Item -Destination $targetFile
# }
# else {
# $_ | Copy-Item -Destination $DestFolder
# }
}
}
Edit
As you commentd you want to remove the leftover .xmr and .att files after copying, here the code that does that too
$today = '{0:yyyyMMdd}' -f (Get-Date)
$SourceFolder = "\\Path1\$today"
$DestFolder = '\\Path2' #Path to Destination Folder
$extensions = 'xmr', 'jpl', 'eml', 'html', 'pdf', 'cml', 'xml', 'att'
# ask for the filename
$baseName = Read-Host 'Please enter the file name without extension'
if (![string]::IsNullOrWhiteSpace($baseName)) {
# construct the filenames to copy
$pattern = $extensions | ForEach-Object { '{0}.{1}' -f $baseName.Trim(), $_ }
# copy all chosen files to the destination folder
(Get-ChildItem -LiteralPath $SourceFolder -Recurse -File) |
Where-Object { $pattern -contains $_.Name } | ForEach-Object {
# rename the extensions by prefixing it with a dollar sign ($)
$targetFile = Join-Path -Path $DestFolder -ChildPath ('{0}.${1}' -f $_.BaseName, $_.Extension.TrimStart("."))
$_ | Copy-Item -Destination $targetFile
# if you want ONLY the .xmr file to be renamed with as .$xmr use this instead:
# if ($_.Extension -eq '.xmr') {
# $targetFile = Join-Path -Path $DestFolder -ChildPath ('{0}.${1}' -f $_.BaseName, $_.Extension.TrimStart("."))
# $_ | Copy-Item -Destination $targetFile
# }
# else {
# $_ | Copy-Item -Destination $DestFolder
# }
# remove leftover .XMR and .ATT files
if ($_.Extension -match '\.(xmr|att)$') { $_ | Remove-Item -Force }
}
}
Good day!!
Maybe we can help you with this:
First of all you need to ask for the path and we have two ways to do this (maybe more) but those are the ways I know
The first option is the simple, using the Read-Host Cmdlet:
$path = Read-Host "Please type the path of the folder"
The second option is more complicated and use dotnet framework: (to ask for the destination just change the last two variables with destination instead of sourcePath)
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$box = New-Object System.Windows.Forms.Form
$box.Text = 'Script Execution'
$box.Size = New-Object System.Drawing.Size(300,200)
$box.StartPosition = 'CenterScreen'
$okButton = New-Object System.Windows.Forms.Button
$okButton.Location = New-Object System.Drawing.Point(75,120)
$okButton.Size = New-Object System.Drawing.Size(75,23)
$okButton.Text = 'OK'
$okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
$box.AcceptButton = $okButton
$box.Controls.Add($okButton)
$cancelButton = New-Object System.Windows.Forms.Button
$cancelButton.Location = New-Object System.Drawing.Point(150,120)
$cancelButton.Size = New-Object System.Drawing.Size(75,23)
$cancelButton.Text = 'Cancel'
$cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$box.CancelButton = $cancelButton
$box.Controls.Add($cancelButton)
$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Point(10,20)
$label.Size = New-Object System.Drawing.Size(280,20)
$label.Text = 'Please type the path of the files:'
$box.Controls.Add($label)
$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Location = New-Object System.Drawing.Point(10,40)
$textBox.Size = New-Object System.Drawing.Size(260,20)
$box.Controls.Add($textBox)
$box.Topmost = $true
$box.Add_Shown({$textBox.Select()})
$result = $box.ShowDialog()
if ($result -eq [System.Windows.Forms.DialogResult]::OK)
{
$sourcePath = $textBox.Text
$sourcePath
}
Once we have the path we can copy the files to do it we can use Copy-item
(I'm not sure if you want to rename the original files or the final files, anyway if you want to rename the last files just use rename-item cmdlet)
$fileList = #(
Get-ChildItem -Path $sourcePath -Recurse -include "*.jpl", "*.eml", "*.html", "*.pdf", "*.cml", "*.xml"
)
foreach ($f in $fileList){
Copy-Item -Path $f.fullname -Destination "$destPath\$f.name"
}
And this should be enough, i'm not really sure about the $fileList part maybe I made some typo, sorry in this case.
Have a nice day!
I'm trying to move files (a bunch of files in a subdirectory), create a subfolder if it doesn't exist and then move the files to the subfolder
$files = Get-ChildItem -Path H:\Movies -File
# for each($file in $files){
$file = $files[0]
$filename = [io.path]::GetFileNameWithoutExtension($file)
$path= "H:\movies\" + $filename
if (!(test-path -Path $path )){ #always returns false?
New-Item -ItemType Directory -Force -Path $path
}
Move-Item $file.FullName $path
#}
I would try something like that:
$path = 'H:\Movies'
$files = Get-ChildItem -Path $path -File
foreach ($file in $files){
# Get filename without extension
$filename = $file.BaseName
# Build new folderpath
$newFolderPath = Join-Path -Path $path -ChildPath $filename
# Check if folder already exists
if (-not(Test-Path -Path $newFolderPath)) {
New-Item -Path $path -Name $filename -ItemType Directory
}
# Move file to new folder
$destination = Join-Path -Path $newFolderPath -ChildPath $file.Name
Move-Item -Path $file.FullName -Destination $destination
}
I have Tested the below code using foreach and working fine for me.
$files = Get-ChildItem -Path E:\Test -File
foreach($file in $files){
#Write-Output $file.Name
$filename = [io.path]::GetFileNameWithoutExtension($file)
Write-Output $filename
$path= "E:\Test\" + $filename
Write-Output $path
if (!(test-path -Path $path )){
Write-Output "Not Exists: $path"
#New-Item -ItemType Directory -Force -Path $path
New-Item -ItemType Directory -Path $path
}
Move-Item $file.FullName $path
}
I gave up on powershell and used file2foldergui instead
I have a folder hierarchy with a lot of files.
I need to copy all folders and only selected files. For this purposes I write script:
$path = "D:\Drop\SOA-ConfigurationManagement - Test\181"
$files = Get-ChildItem -Path $path -Recurse | ? { $_.Name -like "system.serviceModel.client.config" }
$Destination = "D:\test\"
Copy-Item $files -Destination $Destination -recurse
When I execute variable $files, it returns correct path:
But when I execute Copy-Item it returns not full path:
Perhaps my approach is wrong. If so, how to copy entire folder structure, and only selected files (in this case system.serviceModel.client.config file)?
UPD1 Ok, I've found, how to copy only folders:
$path = "D:\Drop\SOA-ConfigurationManagement - Test\181\"
$Destination = "D:\test\"
Copy-Item $path $Destination -Filter {PSIsContainer} -Recurse -Force
But how to copy only selected files, preserving their location? What needs to be in $Destination variable?
$files = Get-ChildItem -Path $path -Recurse | ? { $_.Name -like "system.serviceModel.client.config" } | % { Copy-Item -Path $_.FullName -Destination $Destination }
This code would keep the directory structure the same too
$path = "D:\Drop\SOA-ConfigurationManagement - Test\181\"
$Destination = "D:\test\"
$fileName = "system.serviceModel.client.config"
Get-ChildItem -Path $path -Recurse | ForEach-Object {
if($_.Name -like $fileName) {
$dest = "$Destination$(($_.FullName).Replace($path,''))"
$null = New-Item $dest -Force
Copy-Item -Path $_.FullName -Destination $dest -Force
}
}
To copy the whole folder structure AND files with a certain name, below code should do what you want:
$Source = 'D:\Drop\SOA-ConfigurationManagement - Test\181'
$Destination = 'D:\test'
$FileToCopy = 'system.serviceModel.client.config'
# loop through the source folder recursively and return both files and folders
Get-ChildItem -Path $Source -Recurse | ForEach-Object {
if ($_.PSIsContainer) {
# if it's a folder, create the new path from the FullName property
$targetFolder = Join-Path -Path $Destination -ChildPath $_.FullName.Substring($Source.Length)
$copyFile = $false
}
else {
# if it's a file, create the new path from the DirectoryName property
$targetFolder = Join-Path -Path $Destination -ChildPath $_.DirectoryName.Substring($Source.Length)
# should we copy this file? ($true or $false)
$copyFile = ($_.Name -like "*$FileToCopy*")
}
# create the target folder if this does not exist
if (!(Test-Path -Path $targetFolder -PathType Container)) {
$null = New-Item -Path $targetFolder -ItemType Directory
}
if ($copyFile) {
$_ | Copy-Item -Destination $targetFolder -Force
}
}
try this
$path = 'D:\Drop\SOA-ConfigurationManagement - Test\181\'
$Destination = 'D:\test\'
$files = Get-ChildItem -Path $path -Recurse -File | where Name -like "*system.serviceModel.client.config*" | %{
$Dir=$_.DirectoryName.Replace($path, $Destination)
$NewPAthFile=$_.FullName.Replace($path, $Destination)
#create dir if not exists
New-Item -Path $Dir -ItemType Directory -Force -ErrorAction SilentlyContinue
#copy file in new dir
Copy-Item $_.FullName $NewPAthFile
}
With minimal changes I'd suggest the following:
$path = "D:\Drop\SOA-ConfigurationManagement - Test\181"
$files = Get-ChildItem -Path $path -Recurse | ? { $_.Name -like "system.serviceModel.client.config" }
$Destination = "D:\test\"
$files | % { $_ | Copy-Item -Destination $Destination -recurse }
You can even put the whole copy on one line:
$path = "D:\Drop\SOA-ConfigurationManagement - Test\181"
$Destination = "D:\test\"
Get-ChildItem -Path $path -Recurse | ? { $_.Name -like "system.serviceModel.client.config" } | % { $_ | Copy-Item -Destination $Destination -recurse }
Copy-Item can find the path from the stream of input objects but it doesn't seem to be able to take a collection of System.IO.FileInfo objects as an argument to Path.
I have a PowerShell script that moves all files from one location to another that have a date modified older than 3 years. I have it so the file when moved to the new location also keeps the file structure of the original.
I am trying to make it so once the file has been moved to the new location it creates a shortcut in the original directory which points to the new location of the file.
Below is my script so far which does all the above minus the shortcut.
$sourceDir = "C:\Users\bgough\Documents\powershell\docs"
$archiveTarget = "C:\Users\bgough\Documents\archive"
$dateToday = Get-Date
$date = $dateToday.AddYears(-3)
$items = Get-ChildItem $sourceDir -Recurse |
Where-Object {!$_.PSIsContainer -and $_.LastWriteTime -le $date}
foreach ($item in $items)
{
$withoutRoot = $item.FullName.Substring([System.IO.Path]::GetPathRoot($item.FullName).Length);
$destination = Join-Path -Path $archiveTarget -ChildPath $withoutRoot
$dir = Split-Path $destination
if (!(Test-Path $dir))
{
mkdir $dir
}
Move-Item -Path $item.FullName -Destination $destination
$WshShell = New-Object -ComObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut("$sourceDir")
$Shortcut.TargetPath = $destination
$Shortcut.Save()
}
In my script I have included my attempt at creating this shortcut but it hasn't helped. I have also read through the following but don't understand it too well..
How to create a shortcut using Powershell
Powershell Hard and Soft Links
Edit:
I have successfully got the shortcut to create and in the original folder. However, I can't seem to figure out how to pass a variable to use as the shortcut name. At the moment a string is hard coded, which is what the shortcut gets named. Please see code below: I would like to set the name as the item full name (Same name as document that was moved).
$sourceDir = "C:\Users\bgough\Documents\powershell\docs"
$archiveTarget = "C:\Users\bgough\Documents\archive"
$dateToday = Get-Date
$date = $dateToday.AddYears(-3)
$items = Get-ChildItem $sourceDir -recurse | Where-Object {!$_.PsIsContainer -and $_.LastWriteTime -le $date}
foreach ($item in $items)
{
$withoutRoot = $item.FullName.Substring([System.IO.Path]::GetPathRoot($item.FullName).Length);
$destination = Join-Path -Path $archiveTarget -ChildPath $withoutRoot
$dir = Split-Path $destination
if (!(Test-Path $dir))
{
mkdir $dir
}
Move-Item -Path $item.FullName -Destination $destination
$wshshell = New-Object -ComObject WScript.Shell
$desktop = [System.Environment]::GetFolderPath('Desktop')
$lnk = $wshshell.CreateShortcut($sourceDir + "\ShortcutName.lnk")
$lnk.TargetPath = "$destination"
$lnk.Save()
}
.lnk files are fine when you're using Explorer but they don't play well in Powershell or a command prompt.
What you need to do is create a symbolic link for the file. You can't do this in Powershell, but there is a command line utility called mklink that does it. I've wrapped it in a function so that you can call it:
function CreateLink
{
param
(
[string] $LinkName,
[string] $TargetFile
)
&"cmd.exe" /c mklink "$LinkName" "$TargetFile" | Out-Null
}
In your example you would call it like this:
CreateLink -LinkName $item.FullName -TargetFile $destination
When you look at the directory in Powershell the file will show up as being 0 bytes in size. Don't worry about that.
Thanks for your script Android Magic.
I have modified it to:
Copy a set of files from source to destination
It creates the identical folder structure on the destination, even if the folders are empty
It then creates a symbolic link to the archived file. SymbolicLink support was added in Powershell v5.1. You have to run the script as Admin in order for the Symbolic Link creation to work.
I'd like to add a function to email if anything goes wrong and a summary of status, but that's for another day.
$sourceDir = "\\Fileserver1\IT\Vendor"
$archiveTarget = "\\FS-ARCHIVE\Archive\Fileserver1\IT\Vendor"
$rootArchivePath = "\\FS-ARCHIVE\Archive"
$dateToday = Get-Date
$date = $dateToday.AddYears(-3)
# Copy folder structure to Archive
Get-ChildItem -Path $sourceDir -Recurse |
?{ $_.PSIsContainer } |
Copy-Item -Destination {Join-Path $archiveTarget $_.Parent.FullName.Substring($sourceDir.length)} -Force
$items = Get-ChildItem $sourceDir -Recurse -Attributes !Directory |
Where-Object {$_.LastAccessTime -le $date}
foreach ($item in $items)
{
$withoutRoot = Split-Path -Path $item.FullName
$destination = $rootArchivePath + $withoutRoot.Remove(0,1)
$destFile = $destination + "\" + $item
Move-Item -Force -Path $item.FullName -Destination $destination -Verbose
New-Item -ItemType SymbolicLink -Path $withoutRoot -Name $item -Value $destFile -Force -Verbose
}
How do I get this powershell program to successfully execute?
$Source = "\\ahs-bind01\ftptest01\CRAR"
Get-ChildItem -Path $Source -recurse
#Declare the file path and sheet name
$file = "C:\Users\us6205\Desktop\DatabaseNameConfigs\Test\CareMoveFileParentDestPaths.xlsx"
$sheetName = "sheet1"
#Create an instance of Excel.Application and Open Excel file
$objExcel = New-Object -ComObject Excel.Application
$workbook = $objExcel.Workbooks.Open($file)
$sheet = $workbook.Worksheets.Item($sheetName)
$objExcel.Visible=$false
#Count max row
$rowMax = ($sheet.UsedRange.Rows).count
#Declare the starting positions
$rowLOC,$colLOC = 1,1
$rowUNC,$colUNC = 1,2
#loop to get values and store it
for ($i=1; $i -le $rowMax-1; $i++)
{
$LOC = $sheet.Cells.Item($rowLOC+$i,$colLOC).text
$UNC = $sheet.Cells.Item($rowUNC+$i,$colUNC).text
$Path = Get-ChildItem -Path $UNC -recurse |
Where-Object { ($_.PSIsContainer -eq $true) -and ( $_.FullName -match "$Source") }
$Dest = $Path | New-Item -path $Dest -itemtype directory | Move-Item -Path $Source -Destination $Dest -Force
}
#close excel file
$objExcel.quit()
You want to put these $Source = "\ahs-bind01\ftptest01\CRAR" Get-ChildItem -Path $Source -recurse each to its line like this:
$Source = "\ahs-bind01\ftptest01\CRAR"
Get-ChildItem -Path $Source -recurse
Same with $rowLOC,$colLOC = 1,1 $rowUNC,$colUNC = 1,2
$rowLOC,$colLOC = 1,1
$rowUNC,$colUNC = 1,2
Same with $LOC = $sheet.Cells.Item($rowLOC+$i,$colLOC).text $UNC = $sheet.Cells.Item($rowUNC+$i,$colUNC).text
$LOC = $sheet.Cells.Item($rowLOC+$i,$colLOC).text
$UNC = $sheet.Cells.Item($rowUNC+$i,$colUNC).text
Also edit ($.PSIsContainer -eq $true) -and ( $.FullName -match "$Source") to:
($_.PSIsContainer -eq $true) -and ( $_.FullName -match "$Source")