How to use dash or hyphen to join a string array in Powershell - string

I wrote code like this:
$count = 0
$path = "C:\Videos\"
$oldvids = Get-ChildItem -Path $path -Include *.* -Recurse
foreach ($oldvid in $oldvids) {
$curpath = $oldvid.DirectoryName
$name = [System.IO.Path]::GetFileNameWithoutExtension($oldvid)
$names = $name.Split(" - ")
$names[0] = ""
$metadata_title = $names -join "-"
$ext = [System.IO.Path]::GetExtension($oldvid)
if ($name.StartsWith("new_") -eq $false)
{
$newvid = $curpath + "/new_" + $name + ".mp4"
if ([System.IO.File]::Exists($newvid) -eq $false)
{
$count++
Write-Output $metadata_title
}
}
}
But this code causes a file name like this:
Chapter 1 - New Video
to become:
Chapter 1---New Video
How can I make sure a single - is actually only one? Do I have to escape it?
The idea is to eliminate first part of the file names, so from:
01 - Chapter 1 - Video 1
to:
Chapter 1 - Video 1
So I wanted to split using " - " and then join everything back without the first element in the split array.

Looking at your example and your explanation of changing metadata with ffmpeg on each file, I guess this is what you need:
$count = 0
$path = 'C:\Videos'
# get a list of old video files (these do not start with 'new_')
$oldvids = Get-ChildItem -Path $path -Filter *.mp4 -File -Recurse |
Where-Object { $_.Name -notmatch '^new_' }
foreach ($oldvid in $oldvids) {
# if the file is called 'C:\Videos\01 - Chapter 1 - Video 1.mp4'
$tempName = $oldvid.Name -replace '^\d+\s*-\s*(.+)', 'new_$1' # --> new_Chapter 1 - Video 1.mp4
# or do
# $tempName = 'new_' + ($oldvid.Name -split '-', 2)[-1].Trim() # --> new_Chapter 1 - Video 1.mp4
# or
# $tempName = $oldvid.Name -replace '^\d+\s*-\s*', 'new_' # --> new_Chapter 1 - Video 1.mp4
# combine the current file path with the temporary name
$outputFile = Join-Path -Path $oldvid.DirectoryName -ChildPath $tempName
#######################################################################
# next do your ffmpeg command to change metadata
# for input you use $oldvid.FullName and for output you use $outputFile
Write-Host "Updated file $($oldvid.Name) as $tempName"
#######################################################################
# when done with ffmpeg, delete the original (or for safety move it to somewhere else)
Write-Host "Deleting file '$($oldvid.Name)'"
$oldvid | Remove-Item -WhatIf
# and rename the updated file by removing the 'new_' part from its name
$newName = ($tempName -replace '^new_').Trim()
Write-Host "Renaming updated file to '$newName'"
$tempName | Rename-Item -NewName $newName
# all done, proceed with the next file
$count++
}
Note: I have added switch -WhatIf to the Remove-Item line. This is a safety measure that will only display what file would be deleted without actually deleting it.
If you are sure the correct file should be deleted, then remove that -WhatIf switch so the original file gets destroyed after maipulating it with ffmpeg.
As per your comment, to send items to the Recycle bin instead of destroying them like Remove-Item does, here's two ways of achieving that:
Method 1: Use COM
function RemoveTo-RecycleBin {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Alias('FullName')]
[string[]]$Path
)
begin {
$shell = New-Object -ComObject 'Shell.Application'
$Recycler = $Shell.NameSpace(0xa)
}
process {
foreach ($item in $Path) {
[void]$Recycler.MoveHere($item)
}
}
end {
# clean-up the used COM objects
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($Recycler)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($shell)
$null = [System.GC]::Collect()
$null = [System.GC]::WaitForPendingFinalizers()
}
}
# usage example, remove all files from the D:\Test directory
Get-ChildItem -Path 'D:\Test' -Filter '*.*' -File | RemoveTo-RecycleBin
# usage example, remove all files and subdirectories from the D:\Test directory
Get-ChildItem -Path 'D:\Test' | RemoveTo-RecycleBin
Method 2: Use the Microsoft.VisualBasic assembly
function RemoveTo-RecycleBin {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Alias('FullName')]
[string[]]$Path,
[switch]$ShowConfirmationDialog
)
begin {
Add-Type -AssemblyName Microsoft.VisualBasic
$showUI = if ($ShowConfirmationDialog) { 'AllDialogs' } else { 'OnlyErrorDialogs' }
}
process {
foreach ($item in $Path) {
Write-Host $item
# detect if this is a file or a directory
if ((Get-Item -Path $item) -is [System.IO.DirectoryInfo]) {
# first parameter: the absolute full path
# second parameter: one of Microsoft.VisualBasic.FileIO.UIOption values: OnlyErrorDialogs or AllDialogs
# third parameter: one of Microsoft.VisualBasic.FileIO.RecycleOption values: DeletePermanently or SendToRecycleBin
[Microsoft.VisualBasic.FileIO.FileSystem]::DeleteDirectory($item, $showUI, 'SendToRecycleBin')
}
else {
# first parameter: the absolute full path and file name
# second parameter: one of Microsoft.VisualBasic.FileIO.UIOption values: OnlyErrorDialogs or AllDialogs
# third parameter: one of Microsoft.VisualBasic.FileIO.RecycleOption values: DeletePermanently or SendToRecycleBin
[Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile($item,$showUI, 'SendToRecycleBin')
}
}
}
}
# usage example, remove all files from the D:\Test directory
Get-ChildItem -Path 'D:\Test' -Filter '*.*' -File | RemoveTo-RecycleBin
# usage example, remove all files and subdirectories from the D:\Test directory
Get-ChildItem -Path 'D:\Test' | RemoveTo-RecycleBin
Just choose any of the above functions, put it on top of your script and then change line
$oldvid | Remove-Item -WhatIf
into
$oldvid | RemoveTo-RecycleBin

Related

I am parsing RoboCopy logs from Millions of files, How can I make my code run Faster?

New to StackOverflow, I'll do my best to post correctly :)
Hoping someone can help me to get my code running faster.
The code is run against RoboCopy Migration logs from a massive DFS server migration (20 DFS servers being migrated).
The code first captures the source/destination of the log in question and then looks for the 'Newer', 'Older', 'New File' and 'Extra File' entries/rows. It then checks to see if these files exist at each side, what attributes they have and does a DFSR hash check against both sides (as the files are now being replicated via DFSR).
The main concern is if the hashes match for source and destination and if the temporary attribute is in place.
The problem I am having is that there are millions of files logged under these types (the migration was gargantuan) so the script is taking forever to run. To add to this the client will not allow ports for psremoting/invoke-command.
At present I am running my code without multi-threading, with a copy on each of the DFS servers looking at their respective logs but it is still slow.
I have been looking at running a foreach parallel on looping through each log row (not the loop of log files) but:
With so much data within each log/loop my understanding is that I have to write it out rather than keep it in an PsCustomObject? Otherwise I would run out of RAM?
I don't really understand how to use MUTEXes to get multiple writes to the CSV.
Can someone please advise me on the above 2 points? And maybe give me some more ideas on what I can do to optimise things?
My full code is below..
#Get Start Time
$ReportStartTime = (Get-Date).ToString('yyy-MM-dd_HH-mm-ss')
If(!(test-path "C:\Temp\MasterReport_$ReportStartTime\")){
new-item -type directory -path "C:\Temp\MasterReport_$ReportStartTime\" | Out-Null
}
"Script Started:$ReportStartTime" >> "C:\Temp\MasterReport_$ReportStartTime\Log_$ReportStartTime.Log"
#Get Logs from folder (Recursive)
$Logs = Try{
Get-ChildItem -path 'C:\Temp\RoboCopyLogs\*\*.log' -Recurse -ErrorAction Stop | Select FullName
}
catch{
$_.Exception >> "C:\Temp\MasterReport_$ReportStartTime\Errors_$ReportStartTime.log"
}
#Initialise Log Counters
$NumberOfFiles = 0
$DesktopFile = 0
$ProcessedFiles = 0
$Totalsize = 0
#Count Logs
$Logs | foreach {
$SourceLog = $_
#Get Logfile
$Log = Get-Content $SourceLog.FullName
#Get Log rows for required Error Types and begin loop
$Log | Select-String -Pattern '(^\t+ +(Newer|Older|New File|Extra File))' `
|foreach {
$NumberOfFiles=$NumberOfFiles+1
If($_ | Select-String -pattern 'Desktop.ini' -SimpleMatch){
$DesktopFile=$DesktopFile+1
}
}
}
$Expected = $NumberOfFiles - $DesktopFile
"Total Files To Check = $NumberOfFiles" >> "C:\Temp\MasterReport_$ReportStartTime\Log_$ReportStartTime.Log"
"Total Files Excluded = $DesktopFile" >> "C:\Temp\MasterReport_$ReportStartTime\Log_$ReportStartTime.Log"
"Total Files To Ingest = $Expected" >> "C:\Temp\MasterReport_$ReportStartTime\Log_$ReportStartTime.Log"
$Main = (Get-Date).ToString('yyy-MM-dd_HH-mm-ss')
"Main Script:$Main" >> "C:\Temp\MasterReport_$ReportStartTime\Log_$ReportStartTime.Log"
$Logs | foreach {
$SourceLog = $_
#Get Logfile
$Log = Get-Content $SourceLog.FullName
#Collect Source and Destination
$S = $Log | Select-String -Pattern 'Source :'
$D = $Log | Select-String -Pattern 'Dest :'
$SourceLocation = $S -replace '\s+Source : ',''
$DestLocation = $D -replace '\s+Dest : ',''
#Get Log rows for required Error Types and begin loop
$Log | Select-String -Pattern '(^\t+ +(Newer|Older|New File|Extra File))' | Select-String -pattern 'Desktop.ini' -SimpleMatch -NotMatch `
|foreach {
#This loop could be a foreach -parallel???
#Check Percent Completed
If($ProcessedFiles>0){
$PercentComplete=[Math]::Ceiling(($ProcessedFiles/$Expected)*100)
If($PercentComplete -match ('([0-9]0)')){
"$($PercentComplete)% Completed" > "C:\Temp\MasterReport_$ReportStartTime\PercentComplete.Log"
($ProcessedFiles/$Expected)*100
}
}
#Count Logs Processed
$ProcessedFiles=$ProcessedFiles+1
#Populate FilePath
$FilePath = $_ -Replace '.*(?=\\\\)', ''
#Populate Error type
$RoboErrorRaw = $_ -replace '\s+','|'
$RoboError = $RoboErrorRaw.split("|")[1]
#Check if file path relates to Source or the Destination and set path variables
if($FilePath -like "$SourceLocation*"){
$SourceFilePath = $FilePath
$DestFilePath = $FilePath.replace($SourceLocation,$DestLocation)
}
Elseif($FilePath -like "$DestLocation*"){
$DestFilePath = $FilePath
$SourceFilePath = $FilePath.replace($DestLocation,$SourceLocation)
$IsAtPartner = Test-Path $SourceFilePath
}
Else{
$DestFilepath = "Could Not Resolve UNC to Source or Destination"
}
#Check if file exists at source and destination
Try{
$IsAtPartner = Test-Path $DestFilePath -ErrorAction Stop
}
catch{
$IsAtPartner = $_.Exception
}
Try{
$IsAtSource = Test-path $SourceFilePath -ErrorAction Stop
}
catch{
$IsAtSource = $_.Exception
}
If($IsAtSource){
#Get the file details
Try{
$SourceFileDetails = Get-ChildItem $FilePath -Hidden -ErrorAction Stop
}
catch{
$SourceFileDetails = 'Failed'
}
if($SourceFileDetails -ne 'Failed'){
#Check has temp attribute
if((($SourceFileDetails).Attributes -band 0x100) -eq 0x100){
$TempAttribute = "Yes"
}
Else{
$TempAttribute = "No"
}
#Get attributes and last modified
Try{
$AllAttributes = ($SourceFileDetails).Attributes
}
catch{
$AllAttributes = $_.Exception
}
Try{
$Modified = ($SourceFileDetails).LastWriteTime.ToString()
}
catch{
$Modified = $_.Exception
}
}
}
#Check if .bak file
if($filePath -match '\.bak$'){
$Bakfile = "Yes"
}
Else{
$Bakfile = "No"
}
#Get Hashes
If($IsAtPartner -and $IsAtSource){
$HashSource = (Get-DfsrFileHash -Path $SourceFilepath).FileHash
$HashDest = (Get-DfsrFileHash -Path $DestFilepath).FileHash
}
ElseIf(!$IsAtSource -and !$IsAtPartner){
$HashSource = 'File Does not Exist at Source'
$HashDest = 'File Does not Exist At Partner'
}
ElseIf(!$IsAtPartner){
$HashSource = (Get-DfsrFileHash -Path $SourceFilepath).FileHash
$HashDest = 'File Does not Exist At Partner'
}
ElseIf(!$IsAtSource){
$HashSource = 'File Does not Exist at Source'
$HashDest = (Get-DfsrFileHash -Path $DestFilepath).FileHash
}
Else{
$HashSource = 'ERROR'
$HashDest = 'ERROR'
}
#Compare Valid Hashes
If($HashSource -eq $HashDest){
$HashMatch = 'Yes'
}
Else{
$HashMatch = 'No'
}
#Check Filesize where hashes do not match
If($HashMatch = 'No'){
$FileSizeMB = ($SourceFileDetails).length/1MB
}
#Create output object
$Obj = [PSCustomObject]#{
ErrorType = $RoboError
FilePath = $SourceFilePath
PartnerUNC = $DestFilePath
IsAtSource = $IsAtSource
IsAtDestination = $IsAtPartner
BakFile = $Bakfile
TepmpAttribute = $TempAttribute
LastModified = $Modified
AllAttributes = $AllAttributes
HashSource = $HashSource.FileHash
HashDest = $HashDest.FileHash
HashMatch = $HashMatch
RoboSource = $SourceLocation
RoboDest = $DestLocation
FileSizeMB = $FileSizeMB
SourceLog = $SourceLog.FullName
}
$Source = $SourceLocation.split('\\')[2]
$Destination = $DestLocation.split('\\')[2]
if(!(test-path "C:\Temp\$($Source)-$($Destination)_$($ReportStartTime)")){
new-item -type directory -path "C:\Temp\$($Source)-$($Destination)_$($ReportStartTime)" | Out-Null
}
#export to csv
$obj | Export-Csv -Path "C:\Temp\$($Source)-$($Destination)_$($ReportStartTime)\RoboCopyLogChecks_$ReportStartTime.csv" -NoTypeInformation -Append
$obj | Export-Csv -Path "C:\Temp\MasterReport_$ReportStartTime\RoboCopyLogChecks_$ReportStartTime.csv" -NoTypeInformation -Append
#Increment total size of data
If($HashMatch -eq "Yes"){
$Totalsize = $Totalsize + $SourceFileDetails.Length
}
clear-variable -name RoboError,SourceFilePath,DestFilePath,IsAtSource,IsAtPartner,Bakfile,TempAttribute,Modified,AllAttributes,HashSource,HashDest,HashMatch,FileSizeMB,Source,Destination
if($SourceFileDetails){
Remove-Variable -name SourceFileDetails
}
}
}
$Completion = (Get-Date).ToString('yyy-MM-dd_HH-mm-ss')
"Script Completed:$Completion Excluded Processed = $DesktopFile ,Total Processed = $ProcessedFiles" >> "C:\Temp\MasterReport_$ReportStartTime\Log_$ReportStartTime.Log"
"Files without Matching Hashses amount to $($Totalsize/1GB)GB" >> "C:\Temp\MasterReport_$ReportStartTime\Log_$ReportStartTime.Log"
Here is some example log data (could be put in C:\Temp\RoboCopyLogs\Logs\ to run with above code)
-------------------------------------------------------------------------------
ROBOCOPY :: Robust File Copy for Windows
-------------------------------------------------------------------------------
Started : 24 April 2022 17:29:57
Source : \\Test01\
Dest : \\Test02\
Files : *.*
Exc Files : ~*.*
*.TMP
Exc Dirs : \\Test01\DfsrPrivate
Options : *.* /FFT /TS /L /S /E /DCOPY:DA /COPY:DAT /PURGE /MIR /B /NP /XJD /MT:8 /R:0 /W:0
------------------------------------------------------------------------------
Newer 30720 2021/07/20 14:49:36 \\Test01\Test2121.xls
Older 651776 2020/10/25 21:49:32 \\Test01\testppt.ppt
Older 94720 2019/06/10 11:46:03 \\Test01\Thumbs.db
*EXTRA File 1.7 m 2020/09/17 10:36:57 \\Test02\months.jpg
*EXTRA File 1.8 m 2020/09/17 10:36:57 \\Test02\happy.jpg
New File 6421 2020/10/26 10:32:43 \\Test01\26-10-20.pdf
New File 6321 2020/10/26 10:32:43 \\Test01\Testing20.pdf

Functions using multiple Foreach-Object

I have a part of my script that I can't get to work. The goal is, to take files from a folder, filter and organise them by an aspect of their filename, and move them to a new folder which has had new directories made for them. i.e organised by month and year based on file name. E.g. 032 Approved warranty - Croatia - Case-2019 08-1419032, goes into a directory 2019, then 08.
The next step was creating a select all function, which cycled through numbers 01-12. Which it does just fine. Now the issue is I want to cycle for each year as well between 2017-2019. Which is where i'm stuck.
This is my code which does work but only does all months and 1 selected year:
function DoWork { param ([int]$Month)
$StrMonth = $Month.ToString("00")
Echo $StrMonth.ToString("00")
$files = Get-ChildItem $destinationpath -Filter "*$group1 $StrMonth*" -Recurse
foreach ($file in $files)
{
$year = $group1.ToString()
$month = $Month.ToString()
$file.Name
$year
$StrMonth
# Set Directory Path
$Directory = $targetPath + "\" + $year + "\" + $StrMonth
# Create directory if it doesn't exsist
if (!(Test-Path $Directory))
{
New-Item $directory -type directory
}
# Move File to new location
$file | move-Item -Destination $Directory -Force
}
}
if ($group -eq 'Select All') {
1..12 | ForEach-Object {DoWork($_)}
} else {
DoWork($group)
}
I want it too be able to repeat this for multiple years (Select All).
This code was suggested but doesn't work:
function DoWork {
Param([int]$Month,[int]$Year)
$StrMonth = $Month.ToString("00")
echo $StrMonth.ToString("00")
$StrYear = $Year.ToString
echo $StrYearToString
$files = Get-ChildItem $destinationpath -Filter "*$StrYear $StrMonth*" -Recurse
foreach ($file in $files) {
$year = $Year.ToString()
$month = $Month.ToString()
$file.Name
$StrYear
$StrMonth
# Set Directory Path
$Directory = $targetPath + "\" + $StrYear + "\" + $StrMonth
if (!(Test-Path $Directory)) {
New-Item $directory -Type Directory
}
$file | Copy-Item -Destination $Directory -Force
}
}
if ($group1 -eq 'Select All') {
2017..2019 | ForEach-Object {
$year = $_
1..12 | ForEach-Object {DoWork($_, $year)}
}
} elseif ($group -eq 'Select All') {
1..12 | ForEach-Object {DoWork($_, $group1)}
} else {
DoWork($group, $group1)
}
I have found that param block in your function is not correct, define param block as below:
Param([int]$Month,[int]$Year)

PoshRSJob Looping through file directory

I am trying to loop through a directory (sorting by smallest file), get the path, and the file name and then pump those results into a utility.exe program.
I am trying to do this multi threading with PoshRSJob, but I am not even seeing the utility program show up in task manager, I am getting an error "A null key is not allowed in a hash literal.", for every file that exists (if 50 files are in the directory, then I get 50 errors). I also cannot test if the throttling is working, because nothing is actually running.
Import-Module C:\PoshRSJob.psm1
Function MultiThread($SourcePath,$DestinationPath,$CommandArg, $MaxThreads){
if($CommandArg -eq "import") {
$fileExt = "txt"
}else{
$fileExt = "ini"
}
$ScriptBlock = {
Param($outfile, $cmdType, $fileExtension)
[pscustomobject] #{
#get the full path
$filepath = $_.fullname
#get file name (minus extension)
$filename = $_.basename
#build output directory
$destinationFile = "$($outfile)\$($filename).$($fileExtension)"
#command to run
$null = .\utility.exe $cmdType -source `"$filepath`" -target `"$destinationFile`"
}
}
#get the object of the passed source directory, and pipe it into start-rsjob
Get-ChildItem $SourcePath | Sort-Object length | Start-RSJob -ScriptBlock $ScriptBlock -ArgumentList $DestinationPath, $CommandArg, $fileExt -Throttle $MaxThreads
Wait-RSJob -ShowProgress | Receive-RSJob
Get-RSJob | Receive-RSJob
}
MultiThread "D:\input" "D:\output" "import" 3
Your scriptblock is creating an object where you are defining $null = .\utility.exe +++ as a property. As it says, value of $null (nothing) can't be a property name.. I would suggest just running the lines..
You might also want to change the Wait-RSJob-part. You don't specify a job, so it never waits for anything. Try:
Try changing the scriptblock to:
Import-Module C:\PoshRSJob.psm1
Function MultiThread($SourcePath,$DestinationPath,$CommandArg, $MaxThreads){
if($CommandArg -eq "import") {
$fileExt = "txt"
}else{
$fileExt = "ini"
}
$ScriptBlock = {
Param($outfile, $cmdType, $fileExtension)
#get the full path
$filepath = $_.fullname
#get file name (minus extension)
$filename = $_.basename
#build output directory
$destinationFile = "$($outfile)\$($filename).$($fileExtension)"
#command to run
$null = .\utility.exe $cmdType -source `"$filepath`" -target `"$destinationFile`"
}
#get the object of the passed source directory, and pipe it into start-rsjob
Get-ChildItem $SourcePath | Sort-Object length | Start-RSJob -ScriptBlock $ScriptBlock -ArgumentList $DestinationPath, $CommandArg, $fileExt -Throttle $MaxThreads
Get-RSJob | Wait-RSJob -ShowProgress | Receive-RSJob
}
MultiThread "D:\input" "D:\output" "import" 3

Renaming files using a strings from a txt file

The question might sound confusing but all I really need is the ability to change a file name using an array of strings.
For example:
File 1 contains:
abc1234cd.jpg
abc2543ac.jpg
...
File 2 contains (array/reference)
1234c
2543a
...
The new file name for abc1234cd.jpg should now be 1234c.jpg and so forth.
Is this possible with powershell or any other language to do?
Thanks,
This should do it, assuming the files have a one-to-one match.
# Get contents of file 1
$File1 = Get-Content -Path $PSScriptRoot\File1.txt;
# Get contents of file 2
$File2 = Get-Content -Path $PSScriptRoot\File2.txt;
# Iterate over each item in $File1
foreach ($Item in $File1) {
$FileList = Get-ChildItem -Path "c:\test\$Item*.jpg";
foreach ($File in $FileList) {
# Determine file's new name, based on corresponding value in File2
$NewName = $File.Name -replace $Item, $File2[$File1.IndexOf($Item)];
Write-Host -Object ('Old name: {0}, new name: {1}' -f $Item, $NewName);
}
}
After countless hours, I finally got the code to work.
$dir = 'file.txt'
$backup = 'C:\Users\all users\Desktop\backup'
$file = 'file2.txt'
$files = Import-Csv -Header Name -Path $file
foreach ($line in $files){
$linefiles = Get-ChildItem $dir | where {$_.BaseName.Contains($line.Name)}
$count = 0
foreach ($linefile in $linefiles) {
#do stuff to each file here
$name = $linefile.BaseName
$extension = $linefile.Extension
$newName = $line.Name
Copy-Item -Path "$dir\$linefile" -Destination "$backup\$linefile"
if ($count -gt 0){
Rename-Item -NewName "$newName-$count$extension" -Path "$dir\$linefile"}
else{
Rename-Item -NewName "$newName$extension" -Path "$dir\$linefile"}
$count++}
}

PowerShell Scripts to Look for Excel files with .xls and .xlsx extensions

I have an already written scripts which looks for ONLY an excel file with .xls extension. Now,
I want the same scripts to look for either .xls or .xlsx which ever it finds in that particular location and use it......
The old scripts search for files in the F column only on the excel sheet/file.....This makes the search takes forever, so I would like it to search the files in the F column which ONLY have this path:root_project/Fut_DB_Projects in the E column of the excel sheet.
Pls let me know exactly where to insert it in the scripts. I'm very new to Powershell and your answers would be very much appreciated.
# Creating an object for the Excel COM addin
$excelfile = $args[0]
$folder = $args[1]
echo $excelfile
echo $folder
if($excelfile -ne $Null -and $excelfile.Contains(".xls") -and (Test-Path $excelfile) -eq $True)
{
if($folder -ne $Null -And $folder.Contains("\") -and (Test-Path $folder) -eq $True)
{
$ExcelObject = New-Object -ComObject Excel.Application
# Opening the Workbook
$ExcelWorkbook = $ExcelObject.Workbooks.Open($excelfile)
# Opening the Worksheet by using the index (1 for the first worksheet)
$ExcelWorksheet = $ExcelWorkbook.Worksheets.Item(1)
#The folder where the files will be copied/The folder which will be zipped later
$a = Get-Date
$targetfolder = "C:\"+$a.Month+"_"+$a.Day+"_"+$a.Year+"_"+$a.Hour+$a.Minute+$a.Second
#Check if the folder already exists. Command Test-Path $targetfolder returns true or false.
if(Test-Path $targetfolder)
{
#delete the folder if it already exists. The following command deletes a particular directory
Remove-Item $targetfolder -Force -Recurse -ErrorAction SilentlyContinue
}
#The following command is used to create a particular directory
New-Item -ItemType directory -Path $targetfolder
echo "Temp folder created"
#Declaration of variables, COlumn value = 6 for Column F
$row = 1
$col = 6
# Read a value from the worksheet with the following command
$filename = $ExcelWorksheet.Cells.Item($row,$col).Value2
$filename
#change the folder value below to specify the folder where the powershell needs to search for the filename that it reads from excel file.
$null = ""
# Loop through each row in the excel file.
do
{
$filename = $ExcelWorksheet.Cells.Item($row,$col).Value2
#Checking if value read from Excel is Null
#In powershell operator for NOT EQUAL TO is -ne not <>
if($filename -ne $Null)
{
$filepath = $folder+$filename
#Check if the filepath read from excel is a real filepath OR check if the file exists.
if(Test-Path $filepath)
{
#If the file exists, move it to the folder declared above
# Change the below command to Move-Item if you want to Move the file and not Copy...
Copy-Item $filepath $targetfolder
}
else
{
#$Allfiles = Get-ChildItem -Recurse $folder
#You add the folders to the following list that you want the script to skip as it searches
#for the files through directory and its subdirectories
#eg. $xdir = #("Folder1","Folder2","Folder3")
$xdir = #("Tables","Views","Update","Synonyms","BES","ADTLTransit","Fut_DB_Jobs","Triggers","Scripts")
$remove = [string]::join("|",$xdir)
$Allfiles = Get-ChildItem -Recurse $folder | ? { $_.DirectoryName -notmatch $remove}
Write-Host -Fore Yellow ("Looking through subfolders now")
foreach ($file in $Allfiles)
{
$testpath = $file.FullName
if($testpath.Contains($filename))
{
Write-Host -Fore Yellow ("Found the file in a subfolder at location:" + $testpath)
Move-Item $testpath $targetfolder
}
}
}
}
#incrementing the row variable by 1 to move to the next available row in excel.
$row = $row + 1
}
#while condition evaluates if value read from the excel is null. If null, then the loop breaks.
while($filename -ne $Null)
# Important: The object needs to quit and the variables release, otherwise
# an Excel.exe will remain open.
$ExcelObject.Quit()
$ExcelObject = $null
$ExcelWorkbook = $null
$ExcelWorksheet = $null
[GC]::Collect()
do
{
}
while((Test-Path -path $targetfolder) -ne $True)
$targetfolderinfo = Get-ChildItem -Recurse $targetfolder | Measure-Object
$targetfolderfilecount = $targetfolderinfo.count
if($targetfolderfilecount -ne 0)
{
#Following command calls the function to zip all the files moved from source folder to target folder
$directory = [IO.DirectoryInfo] $targetfolder
If ($directory -eq $null)
{
Throw "Value cannot be null: directory"
}
Write-Host ("Creating zip file for folder (" + $directory.FullName + ")...")
[IO.DirectoryInfo] $parentDir = $directory.Parent
[string] $zipFileName
If ($parentDir.FullName.EndsWith("\") -eq $true)
{
# e.g. $parentDir = "C:\"
$zipFileName = $parentDir.FullName + $directory.Name + ".zip"
}
Else
{
$zipFileName = $parentDir.FullName + "\" + $directory.Name + ".zip"
}
If (Test-Path $zipFileName)
{
Remove-Item $zipFileName -Force -Recurse -ErrorAction SilentlyContinue
}
Set-Content $zipFileName ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))
$shellApp = New-Object -ComObject Shell.Application
$zipFile = $shellApp.NameSpace($zipFileName)
If ($zipFile -eq $null)
{
Throw "Failed to get zip file object."
}
[int] $expectedCount = (Get-ChildItem $directory -Force -Recurse).Count
$expectedCount += 1 # account for the top-level folder
$zipFile.CopyHere($directory.FullName)
Write-Host -Fore Green ("Successfully created zip file for folder (" `
+ $directory.FullName + ").")
}
else
{
Write-Host -Fore Red ("There are no files to be zipped")
}
[System.Threading.Thread]::Sleep(10000)
Remove-Item $targetfolder -recurse
}
else
{
Write-Host -Fore Red ("Target folder path not specified correctly")
}
}
else
{
Write-Host -Fore Red ("Excel file path not specified")
}
Try changing the first if to this
if($excelfile -ne $Null -and $excelfile -like "*.xls*" -and (Test-Path $excelfile) -eq $True)

Resources