I'm looking to use powershell to see if there is a specific file in an ftp folder. More specifically, there is roughly 15 different folders that need to be checked for a specific file name. Any ideas on how I would do this?
There is a PowerShell ftp module here.
$DEBUG = 1
# Machines
$MachineNames = #("machine1","machine2" )
$MachineIPs = #("192.168.1.1","192.168.1.2" )
# Websites
$WebsiteNames = #("website1","website2" )
$WebsiteURLs = #("http://yahoo.com","http://google.com" )
#====== check websites
$i = 0;
foreach ($WebsiteURL in $WebsiteURLs){
# First we create the request.
$HTTP_Request = [System.Net.WebRequest]::Create($WebsiteURL)
# We then get a response from the site.
$HTTP_Response = $HTTP_Request.GetResponse()
# We then get the HTTP code as an integer.
$HTTP_Status = [int]$HTTP_Response.StatusCode
#$HTTP_Response
If ($HTTP_Status -eq 200) {
if ($DEBUG -eq 1) {Write-Host "== " $WebsiteNames[$i] " is OK!" }
}
Else {
if ($DEBUG -eq 1) {Write-Host "==Error: "$WebsiteNames[$i] " may be down!" }
SendEmails $WebsiteNames[$i]
}
# Finally, we clean up the http request by closing it.
$HTTP_Response.Close()
Clear-Variable HTTP_Response
$i = $i + 1
}
#====== check IP
$i = 0;
foreach ($MachineIP in $MachineIPs){
$isValidIP = Test-Connection $MachineIP -Count 1 -Quiet
if ($DEBUG -eq 1) {
$hostn = [System.Net.Dns]::GetHostEntry($MachineIP).HostName
New-Object -TypeName PSObject -Property #{'Host'=$hostn;'IP'=$MachineIP}
}
if (-not($isValidIP)) {
if ($DEBUG -eq 1) {Write-Host "==Error: " $MachineNames[$i] " ("$MachineIPs[$i]") may be down!" }
SendEmails $MachineNames[$i]
}
$i = $i + 1
}
Related
I have a script from here, this is the job :
function CaptureWeight {
Start-Job -Name WeightLog -ScriptBlock {
filter timestamp {
$sw.WriteLine("$(Get-Date -Format MM/dd/yyyy_HH:mm:ss) $_")
}
try {
$sw = [System.IO.StreamWriter]::new("$using:LogDir\$FileName$(Get-Date -f MM-dd-yyyy).txt")
& "$using:PlinkDir\plink.exe" -telnet $using:SerialIP -P $using:SerialPort | TimeStamp
}
finally {
$sw.ForEach('Flush')
$sw.ForEach('Dispose')
}
}
}
I'd like to get his to run against a list of IP addresses while also having a name associated with the IP to set the file name for each file. I was thinking something like $Name = Myfilename and $name.IP = 1.1.1.1 and using those in place of $FileName and $SerialIP, but have yet to be able get anything close to working or find an example close enough to what I'm trying for.
Thanks
Here is one way you could do it with a hash table as Theo mentioned in his helpful comment. Be aware that Jobs don't have a Threshold / ThrottleLimit parameter as opposed to Start-ThreadJob or ForEach-Object -Parallel since jobs run in a different process as you have already commented instead of instances / runspaces, there is no built-in way to control how many Jobs can run at the same time. If you wish have control over this you would need to code it yourself.
# define IPs as Key and FileName as Value
$lookup = #{
'1.2.3.4' = 'FileNameForThisIP'
'192.168.1.15' = 'AnotherFileNameForTHatIP'
}
# path to directory executable
$plink = 'path\to\plinkdirectory'
# path to log directory
$LogDir = 'path\to\logDirectory'
# serial port
$serialport = 123
$jobs = foreach($i in $lookup.GetEnumerator()) {
Start-Job -Name WeightLog -ScriptBlock {
filter timestamp {
$sw.WriteLine("$(Get-Date -Format MM/dd/yyyy_HH:mm:ss) $_")
}
try {
$path = Join-Path $using:LogDir -ChildPath ('{0}{1}.txt' -f $using:i.Value, (Get-Date -f MM-dd-yyyy))
$sw = [System.IO.StreamWriter]::new($path)
$sw.AutoFlush = $true
& "$using:plink\plink.exe" -telnet $using:i.Key -P $using:serialPort | TimeStamp
}
finally {
$sw.ForEach('Dispose')
}
}
}
$jobs | Receive-Job -AutoRemoveJob -Wait
The other alternative to the hash table could be to use a Csv (either from a file with Import-Csv or hardcoded with ConvertFrom-Csv).
Adding here another alternative to my previous answer, using a RunspacePool instance which has built-in a way of concurrency and enqueuing.
using namespace System.Management.Automation.Runspaces
try {
# define number of threads that can run at the same time
$threads = 10
# define IPs as Key and FileName as Value
$lookup = #{
'1.2.3.4' = 'FileNameForThisIP'
'192.168.1.15' = 'AnotherFileNameForTHatIP'
}
# path to directory executable
$plink = 'path\to\plinkdirectory\'
# path to log directory
$LogDir = 'path\to\logDirectory'
# serial port
$port = 123
$iss = [initialsessionstate]::CreateDefault2()
$rspool = [runspacefactory]::CreateRunspacePool(1, $threads, $iss, $Host)
$rspool.ApartmentState = 'STA'
$rspool.ThreadOptions = 'ReuseThread'
# session variables that will be intialized with the runspaces
$rspool.InitialSessionState.Variables.Add([SessionStateVariableEntry[]]#(
[SessionStateVariableEntry]::new('plink', $plink, '')
[SessionStateVariableEntry]::new('serialport', $port, '')
[SessionStateVariableEntry]::new('logDir', $LogDir, '')
))
$rspool.Open()
$rs = foreach($i in $lookup.GetEnumerator()) {
$ps = [powershell]::Create().AddScript({
param($pair)
filter timestamp {
$sw.WriteLine("$(Get-Date -Format MM/dd/yyyy_HH:mm:ss) $_")
}
try {
$path = Join-Path $LogDir -ChildPath ('{0}{1}.txt' -f $pair.Value, (Get-Date -f MM-dd-yyyy))
$sw = [System.IO.StreamWriter]::new($path)
$sw.AutoFlush = $true
& "$plink\plink.exe" -telnet $pair.Key -P $serialPort | TimeStamp
}
finally {
$sw.ForEach('Dispose')
}
}).AddParameter('pair', $i)
$ps.RunspacePool = $rspool
#{
Instance = $ps
AsyncResult = $ps.BeginInvoke()
}
}
foreach($r in $rs) {
try {
$r.Instance.EndInvoke($r.AsyncResult)
$r.Instance.Dispose()
}
catch {
Write-Error $_
}
}
}
finally {
$rspool.ForEach('Dispose')
}
I have file with 80k lines. I want to read each line then look for group name which is between number and backup type ( incr,full or manual). then add group in hash table to corresponding server name. I want use hashtable which has array for group names. this is piece of requirement in my big script.
Input file
rspedw03.corpads.local 3085876532 JC_UNIX_FS01_INCR_DD2 JC_FS_DD2 UNIX_FS01_INCR_DD2 incr 02/23/2022 03/29/2022 03/29/2022 disk 1645592426 backup 3013 MB JCBDD2301P.CORPADS.LOCAL
rsuedw01.corpads.local 1020344 JC_DB_DB2 JC_DB Clone_DR full 02/23/2022 04/04/2022 04/04/2022 disk 1645592431 Clone_DR 997 KB MNBDD3302P.corpads.local
rsuedw01.corpads.local 1020344 JC_DB_DB2 full 02/23/2022 04/04/2022 03/30/2022 disk 1645592431 997 KB JCBDD1300P.corpads.local
rsuedw03.corpads.local 12608 MN_UNIX_NP_7_Days MN_DB Clone_DR full 02/23/2022 04/21/2022 04/21/2022 disk 1645592432 Clone_DR 13 KB JCBDD1300P.corpads.local
'# -split '\r?\n'
output should look like
rspedw03.corpads.local JC_UNIX_FS01_INCR_DD2 JC_FS_DD2 UNIX_FS01_INCR_DD2 MN_UNIX_NP_7_Days MN_DB
rsuedw01.corpads.local JC_DB_DB2 JC_DB Clone_DR
i got so far ,
$out = Get-Content C:\scripts\test1.txt
$ht = #{}
$arr = #()
foreach ( $line in $out)
{
$mn = $line -csplit "incr|full|manual"
$md = $mn[0].split(" ")
}
but some line has one group other might have 4 group how do i capture that ?
Here is my complete code ,
$out=Get-Content C:\anil\scripts\test2.txt
$ht = #{}
$arr = #()
$today = Get-Date
foreach ( $line in $out){
$arr=$line.Split(" ")
if ( $arr[0] -ne "nwsppl300p.corpads.local"){
$mn=$line -csplit "incr|full|manual"
$md=$mn[1] -split "\s{1,}"
if ($line -match '.*( backup |Clone_DR ).*') {$btype=$md[9]} else {$btype=$md[8]}
$clientHostName,$null,$backupPlans = -split $mn[0]
$date =$mn[1].split(" ")[2]
$newdate=[Datetime]::ParseExact($date, 'MM/dd/yyyy', $null)
$diff = New-TimeSpan -Start $today -end $newdate
#### look for one year only ########
if ( $diff.Days -lt 400 ) {
if ( $arr[12] -ne "Clone_DR") {
if ($arr[0] -notin $ht.keys){
$ht[$arr[0]] = #{}
if ($btype -match "DB2") {
$ht[$arr[0]]['Db2size'] = $arr[1]
$ht[$arr[0]]['groups'] = #($backupPlans)
}
if ($btype -match "RMAN") {
$ht[$arr[0]]['RMANsize'] = $arr[1]
$ht[$arr[0]]['groups'] = #($backupPlans)
}
if ($btype -notmatch "RMAN" -and $btype -notmatch "DB2" ){
$ht[$arr[0]]['Filesize'] = $arr[1]
$ht[$arr[0]]['groups'] = #($backupPlans)
}
} else {
if ($btype -match "DB2" -and $arr[1] -gt $ht[$arr[0]]['Db2size'] ) {
$ht[$arr[0]]['Db2size'] = $arr[1]
if ($backupplans -notin $ht[$arr[0]]['groups']) { $ht[$arr[0]]['groups'] += #($backupPlans)}
}
if ($btype -match "RMAN" -and $arr[1] -gt $ht[$arr[0]]['RMANsize']) {
$ht[$arr[0]]['RMANsize'] = $arr[1]
if ($backupplans -notin $ht[$arr[0]]['groups']) { $ht[$arr[0]]['groups'] += #($backupPlans)}
}
if ($btype -notmatch "RMAN" -and $btype -notmatch "DB2" -and $arr[1] -gt $ht[$arr[0]]['Filesize']){
$ht[$arr[0]]['Filesize'] = $arr[1]
if ($backupplans -notin $ht[$arr[0]]['groups']) { $ht[$arr[0]]['groups'] += #($backupPlans)}
}
}
} ###clone_dr
} ###less than 400
} ### chcking for networker server
} #### looping thru file
write-host "=================================In MB ==============================================="
write-host "===ServerName==============OverAllsize======DB2size===========RMANsize========FileSize"
write-host "======================================================================================"
$ht.GetEnumerator()| ForEach-Object {
$total = $_.value.Db2size/1024/1024 + $_.value.RMANsize/1024/1024 + $_.value.Filesize/1024/1024
"{0,-25} {1:n2} {2:n2} {3:n2} {4:n2} {5,-25}" -f $_.name,$total,$($_.value.Db2size/1024/1024),$($_.value.RMANsize/1024/1024),$($_.value.Filesize/1024/1024),$_.value.groups}
Once you've got the part before the type (eg. 'rsuedw01.corpads.local 1020344 JC_DB_DB2 ') split into individual words ('rsuedw01.corpads.local', '1020344', 'JC_DB_DB2'), then you know that the first string is going to be the hostname, the second string is going to be the ignored, and anything else is necessarily a label you want to collect.
Then you just need to ensure that an entry with an associated array exists:
$backupLogLines = #(Get-Content C:\scripts\test1.txt) -match "incr|full|manual"
$planTable = #{}
foreach ($line in $backupLogLines)
{
$metadataParts = $line -csplit "incr|full|manual"
# first string is host name,
# second string is discarded,
# ... and the rest are backup plans
$clientHostName,$null,$backupPlans = -split $metadataParts[0]
if(-not $planTable.Contains($clientHostName)){
# Create new array containing the backup plans for the current
$planTable[$clientHostName] = #($backupPlans)
}
else {
$planTable[$clientHostName] += $backupPlans
}
}
To write the results to a file:
$planTable.GetEnumerator() |ForEach-Object {
$_.Name,$(#($_.Value |Sort-Object -Unique) -join ' ') -join ' '
} |Set-Content path\to\output.txt
Background
I am trying to number each item in a WBS with PowerShell. The WBS is on a spreadsheet. For example, if you have the following WBS (4-level depth) from Wikipedia:
The result should be:
1
1.1
1.1.1
1.1.1.1
1.1.1.2
1.1.1.3
1.1.1.4
1.1.1.5
1.1.1.6
1.1.2
1.1.3
1.1.4
1.2
1.3
1.4
1.5
1.6
1.7
1.8
1.9
1.10
1.11
Problem
I decided to export the WBS to CSV and read it with PowerShell:
Import-Csv -LiteralPath .\WBS.csv -Header Lv1,Lv2,Lv3,Lv4 |
ForEach-Object {
$_ | Add-Member -MemberType NoteProperty -Name Lv1i -Value $null
$_ | Add-Member -MemberType NoteProperty -Name Lv2i -Value $null
$_ | Add-Member -MemberType NoteProperty -Name Lv3i -Value $null
$_ | Add-Member -MemberType NoteProperty -Name Lv4i -Value $null
$_
} | Set-Variable wbs
$Lv1i = 0;
$wbs | ForEach-Object {
if ($_.Lv1 -ne "") {
$Lv1i = $Lv1i + 1;
$_.Lv1i = $Lv1i;
} else {
$_.Lv1i = $Lv1i;
}
}
$Lv2i = 0;
$wbs | ForEach-Object {
if ($_.Lv2 -ne "") {
$Lv2 = $_.Lv2;
$Lv2i = $Lv2i + 1;
$_.Lv2i = $Lv2i;
} else {
if ($_.Lv1 -eq "") {
$_.Lv2i = $Lv2i;
} else {
$Lv2i = 0;
}
}
}
$Lv3i = 0;
$wbs | ForEach-Object {
if ($_.Lv3 -ne "") {
$Lv3 = $_.Lv3;
$Lv3i = $Lv3i + 1;
$_.Lv3i = $Lv3i;
} else {
if (($_.Lv1 -ne "") -or ($_.Lv2 -ne "")) {
$Lv3i = 0;
} else {
$_.Lv3i = $Lv3i;
}
}
}
$Lv4i = 0;
$wbs | ForEach-Object {
if ($_.Lv4 -ne "") {
$Lv4 = $_.Lv4;
$Lv4i = $Lv4i + 1;
$_.Lv4i = $Lv4i;
} else {
if (($_.Lv1 -ne "") -or ($_.Lv2 -ne "") -or ($_.Lv3 -ne "")) {
$Lv4i = 0;
} else {
$_.Lv4i = $Lv4i;
}
}
}
$wbs | ForEach-Object { "{0} {1} {2} {3} `t {4}.{5}.{6}.{7}" -F $_.Lv1, $_.Lv2, $_.Lv3, $_.Lv4,$_.Lv1i, $_.Lv2i, $_.Lv3i, $_.Lv4i } `
| ForEach-Object { $_.Trim(".") }
The code above works for me, but it supports only 4-level depth WBS. I want to improve it to handle any depth. To implement this requirement, I think it has to read the CSV file into a variable-size two-dimensional array. But I could not found the robust (support commas and line breaks in cell) way to do it in PowerShell.
Question
Is there any way to import a CSV into a variable-size two-dimensional array with PowerShell? Cells in the CSV could contain commas, double quotes, or line breaks.
Given this sample input data (sample.csv):
Aircraft System;;;
;Air Vehicle;;
;;Airframe;
;;;Airfram Integration
;;;Fuselage
;;;Wing
;;Propulsion;
;;Vehicle Subsystems;
;;Avionics;
;System Engineering;;
Other;;;
the following PowerShell script
$cols = 5
$data = Import-Csv .\sample.csv -Delimiter ";" -Encoding UTF8 -Header (1..$cols)
$stack = #()
$prev = 0
foreach ($row in $data) {
for ($i = 0; $i -lt $cols; $i++) {
$value = $row.$i
if (-not $value) { continue }
if ($i -eq $prev) {
$stack[$stack.Count-1]++
} elseif ($i -eq $prev + 1) {
$stack += 1
} elseif ($i -lt $prev) {
$stack = $stack[0..($i-1)]
$stack[$stack.Count-1]++
}
$prev = $i
Write-Host $($stack -join ".") $value
}
}
outputs
1 Aircraft System
1.1 Air Vehicle
1.1.1 Airframe
1.1.1.1 Airfram Integration
1.1.1.2 Fuselage
1.1.1.3 Wing
1.1.2 Propulsion
1.1.3 Vehicle Subsystems
1.1.4 Avionics
1.2 System Engineering
2 Other
To save the result, instead of printing it out to the console, e.g. this:
$result = foreach ($row in $data) {
for ($i = 0; $i -lt $cols; $i++) {
# ...
[pscustomobject]#{outline = $($stack -join "."); text = $value}
}
}
would give $result as
outline text
------- ----
1 Aircraft System
1.1 Air Vehicle
1.1.1 Airframe
1.1.1.1 Airfram Integration
1.1.1.2 Fuselage
1.1.1.3 Wing
1.1.2 Propulsion
1.1.3 Vehicle Subsystems
1.1.4 Avionics
1.2 System Engineering
2 Other
Not quite as succinct as #Tomalek's answer, but doesn't use an inner loop and accumulates the results into a variable...
Given:
$csv = #"
"Aircraft System"
, "Air Vehicle"
,, "Airframe"
,,, "Airframe Integration, Assembly, Test and Checkout"
,,, "Fuselage"
,,, "Wing"
,,, "Empennage"
,,, "Nacelle"
,,, "Other Airframe Components 1..n (Specify)"
,, "Propulsion"
,, "Vehicle Subsystems"
,, "Avionics"
, "System Engineering"
, "Program Management"
, "System Test and Evaluation"
, "Training"
, "Data"
, "Peculiar Support Equipment"
, "Common Support Equipment"
, "Operational/Site Activation"
, "Industrial Facilities"
, "Initial Spares and Repair Parts"
"#
the code:
# increase "1..9" to , e.g. "1..99" if you want to handle deeper hierarchies
$headers = 1..9;
$data = $csv | ConvertFrom-Csv -Header $headers;
# this variable does the magic - it tracks the index of the current node
# at each level in the hierarchy - e.g. 1.1.1.5 => #( 1, 1, 1, 5 ). each
# time we find a sibling or a new child we edit this array to append or
# increment the last item.
$indexes = new-object System.Collections.ArrayList;
$depth = 0;
$results = new-object System.Collections.ArrayList;
foreach( $item in $data )
{
# we can't nest by more than one level at a time, so this row must have
# a value at either the same depth as the previous if it's a sibling,
# the next depth if it's the first child, or a shallower index if we've
# reached the end of a nested list.
if( $item.($depth + 1) )
{
# this is the first child node of the previous node
$null = $indexes.Add(1);
$depth += 1;
}
elseif( $item.$depth )
{
# this is a sibling of the previous node, so increment the last index
$indexes[$depth - 1] += 1;
}
else
{
# this is the first item after a list of siblings (e.g. 1.1.2), so we
# need to look at shallower properties until we find a value
while( ($depth -gt 0) -and -not $item.$depth )
{
$indexes.RemoveAt($depth - 1);
$depth -= 1;
}
if( $depth -lt 1 )
{
throw "error - no shallower values found"
}
# don't forget this item is a sibling of the previous node at this level
# since it's not the *first* child, so we need to increment the counter
$indexes[$depth - 1] += 1;
}
$results += $indexes -join ".";
}
produces output:
$results
#1
#1.1
#1.1.1
#1.1.1.1
#1.1.1.2
#1.1.1.3
#1.1.1.4
#1.1.1.5
#1.1.1.6
#1.1.2
#1.1.3
#1.1.4
#1.2
#1.3
#1.4
#1.5
#1.6
#1.7
#1.8
#1.9
#1.10
#1.11
I have a program which checks two paths for files and if there are any files it sends one mail for one person each.
They now need four paths checked and multiple person need a mail for a path.
e.g.
Path 1 - Mail to x, y and z
Path 2 - Mail to a and b
Path 3 - Mail to x and a
Path 4 - Mail to s, r and w
How can I make it easy and most efficient?
$folders = #()
$folders += "\\server\files\Info\test"
$folders += "\\server\files\Info\test2"
$receiver = #()
$receiver += "person1#test.com"
$receiver += "person2#test.com"
$i = 0
$folders | ForEach-Object {
$checkforfiles = $_
$directoryInfo = Get-ChildItem $checkforfiles | Measure-Object
$directoryInfo.count #Returns the count of all of the objects in the directory
$Numberone = $directoryInfo.Count
if ($directoryInfo.count -ge 1){
send-mailmessage -subject "Subject" -Body "There are $checkforfiles files " -from foldercheck#test.com -to $receiver[$i] `
-smtpserver smtp.ser.com
$i = $i+1
}
else{
write-host "nothing to process"
}
}
You could simply extend the array of folders to test and use a switch to determine which users should get an email.
Also, I would advise using splatting on cmdlets with a lot of parameters to make for cleaner code.
# an array of folder paths to test
$folders = "\\server\files\Info\test", "\\server\files\Info\test2", "\\server\files\Info\test3", "\\server\files\Info\test4"
for ($i = 0; $i -lt $folders.Count; $i++) {
# get the file (and folder) count inside
$count = #(Get-ChildItem -Path $folders[$i]).Count
if ($count -gt 0) {
# determine who to email
$to = switch ($i) {
0 { 'personX#test.com', 'personY#test.com', 'personZ#test.com' ; break }
1 { 'personA#test.com', 'personB#test.com' ; break }
2 { 'personX#test.com', 'personA#test.com' ; break }
3 { 'personS#test.com', 'personR#test.com', 'personW#test.com' }
}
# set up a hashtable with parameters for splattting to Send-MailMessage
$mailParams = #{
To = $to
From = 'foldercheck#test.com'
Subject = "Subject"
Body = "There are $count files in folder '$($folders[$i])'"
SmtpServer = 'smtp.ser.com'
# add more parameters here if needed
}
# send the email
Send-MailMessage #mailParams
}
else {
Write-Host "Empty folder '$folders[$i]'; Nothing to process"
}
}
With my code I am trying to create a folder, then open a certain Excel file, edit it, and save it to the location that has been made via the function Dagcontrole_folders_maken . Currently i am using this ForEach in a ForEach loop. But it is not working. This is a simplified version of the complete code. The setup of the functions with the variables are neccesary.
Code to demonstrate problem:
$path_dagelijkse_controle = "C:\Users\Nick\Desktop\Test1",
"C:\Users\Nick\Desktop\Test2",
"C:\Users\Nick\Desktop\Test3"
$list_excels = 'C:\Users\nick\Desktop\Test1\kek3', #pad waar het excel bestand staat die geopend moet worden
'C:\Users\nick\Desktop\Test1\kek4',
'C:\Users\nick\Desktop\Test1\kek5'
function Dagcontrole_folders_maken ($huidige_folder) {
md -Path "$huidige_folder\2020" -Force # Makes all the neccessary folders
}
function Aanpassen_excel_dagcontrole ($path, $huidige_folder) {
$Excel = New-Object -ComObject excel.application
$Excel.visible = $True
$date_today= (get-date).DayOfWeek
$Workbook = $excel.Workbooks.open($path)
$workbook.SaveAs("$huidige_folder\$date_today")
$Excel.Quit()
Remove-Variable -Name excel
[gc]::collect()
[gc]::WaitForPendingFinalizers()
}
Foreach ($i in $path_dagelijkse_controle) {
Dagcontrole_folders_maken $i
Foreach ($a in $list_excels) {
Aanpassen_excel_dagcontrole $a $i
}
}
I'm getting errors on the following part: $workbook.SaveAs("$huidige_folder\$date_today"). Telling it does not have access to the file.
What I am trying to do is save the excel file in the directory that has just been made by the function Dagcontrole_folders_maken. I try this in the second loop with the data that comes from the $path_dagelijkse_controle list
The loop should do the following:
Dagcontrole_folder_maken
makes folder with location: "C:\Users\Nick\Desktop\Test1\2020"
Aanpassen_excel_dagcontrole
$Workbook = $excel.Workbooks.open('C:\Users\nick\Desktop\Test1\kek3')
$workbook.SaveAs('C:\Users\Nick\Desktop\Test1"\2020\20200806')
And after that is should do:
Dagcontrole_folder_maken
makes folder with location: "C:\Users\Nick\Desktop\Test2\2020"
Aanpassen_excel_dagcontrole
$Workbook = $excel.Workbooks.open('C:\Users\nick\Desktop\Test2\kek4')
$workbook.SaveAs('C:\Users\Nick\Desktop\Test2"\2020\20200806')
And then rest of the list
The complete code for reference:
$path_dagelijkse_controle = "C:\Users\Nick\Desktop\Test1",
"C:\Users\Nick\Desktop\Test2",
"C:\Users\Nick\Desktop\Test3"
$list_excels = 'C:\Users\nick\Desktop\Test1\kek3', #pad waar het excel bestand staat die geopend moet worden
'C:\Users\nick\Desktop\Test1\kek4',
'C:\Users\nick\Desktop\Test1\kek5'
function Dagcontrole_folders_maken ($huidige_folder) {
$Dagelijkse_controle = "Dagelijkse controle"
$datum_vandaag = $(Get-Date).toString('yyyy-MM-dd')
$jaar = $datum_vandaag.Substring(0,4)
$maand = $datum_vandaag.substring(5, 2)
$dag = (get-date).DayOfWeek
$folder_maand = Get-Date -UFormat "%m - %B"
md -Path "$huidige_folder\$jaar\$folder_maand\Dagelijks\$datum_vandaag" -Force # Makes all the neccessary folders
}
function Aanpassen_excel_dagcontrole ($path, $huidige_folder) {
#editing excel file
$Controle_mailbox_vrijdag = "Nora Remeeus"
$weekcontrole1 = "Maandag"
$weekcontrole2 = "Dinsdag"
$partimedag = "Woensdag"
$dagcontroleur_parttimedag = "Victor Wong"
$weekcontrole_persoon = "Nick Siegert"
$afwezig_mailboxcontrole = "Vrijdag"
$Excel = New-Object -ComObject excel.application
$Excel.visible = $False
$Workbook = $excel.Workbooks.open($path)
$Worksheet = $Workbook.WorkSheets.item("Uit te voeren werkzaamheden")
$worksheet.activate()
$workbook.ActiveSheet.Cells.Item(3,3) = Date
if ($dag -eq $partimedag) {
$workbook.ActiveSheet.Cells.Item(9,3) = $dagcontroleur_parttimedag
$workbook.ActiveSheet.Cells.Item(10,3) = $dagcontroleur_parttimedag
$workbook.ActiveSheet.Cells.Item(12,3) = $dagcontroleur_parttimedag
}
if (($dag -eq $weekcontrole1) -or ($dag -eq $weekcontrole2)) {
$workbook.ActiveSheet.Cells.Item(13,3) = $weekcontrole_persoon
}
if ($dag -eq $afwezig_mailboxcontrole) {
$workbook.ActiveSheet.Cells.Item(11,3) = $Controle_mailbox_vrijdag
}
$workbook.SaveAs("$huidige_folder\$jaar\$folder_maand\Dagelijks\$datum_vandaag\$Dagelijkse_controle $datum_vandaag") #Edit to save with Dagelijkse controle + datum_vandaag Hardcoded $huidige folder (eerste deel) oud: "$huidige_folder\$jaar\$folder_maand\Dagelijks\$datum_vandaag\$Dagelijkse_controle $datum_vandaag"
$Excel.Quit()
Remove-Variable -Name excel
[gc]::collect()
[gc]::WaitForPendingFinalizers()
}
Foreach ($i in $path_dagelijkse_controle) {
Dagcontrole_folders_maken $i
Foreach ($a in $list_excels) {
Aanpassen_excel_dagcontrole $a $i
}
}
You've got what appears to be a logic and syntax errors in your code.
If you set Excel to not viable, then you cannot set it as the active Window.
So, this...
$Excel.visible = $False
....
$worksheet.activate()
... should be this...
$Excel.visible = $True
You are running this function ...
Aanpassen_excel_dagcontrole
... in a Loop, but this function is opening and closing MSExcel.exe on each pass. This will cause issues because the startup and shutdown of MSOffice products take time, but your loop is not waiting for that.
You should only an Office app once, open, process, close files, and after all are processed, then close the MSOffice app used.
I'd suggest you remove this ...
$Excel.Quit()
Remove-Variable -Name excel
[gc]::collect()
[gc]::WaitForPendingFinalizers()
... from that function and make it your last entry in the script. So, this:
# Start MSExcel only once
$Excel = New-Object -ComObject excel.application
$Excel.visible = $True
function Aanpassen_excel_dagcontrole
{
[cmdletbinding(SupportsShouldProcess)]
Param
(
$path, $huidige_folder
)
# editing excel file
$Controle_mailbox_vrijdag = 'Nora Remeeus'
$weekcontrole1 = 'Maandag'
$weekcontrole2 = 'Dinsdag'
$partimedag = 'Woensdag'
$dagcontroleur_parttimedag = 'Victor Wong'
$weekcontrole_persoon = 'Nick Siegert'
$afwezig_mailboxcontrole = 'Vrijdag'
$Workbook = $excel.Workbooks.open($path)
$Worksheet = $Workbook.WorkSheets.item('Uit te voeren werkzaamheden')
$worksheet.activate()
$workbook.ActiveSheet.Cells.Item(3,3) = Date
if ($dag -eq $partimedag)
{
$workbook.ActiveSheet.Cells.Item(9,3) = $dagcontroleur_parttimedag
$workbook.ActiveSheet.Cells.Item(10,3) = $dagcontroleur_parttimedag
$workbook.ActiveSheet.Cells.Item(12,3) = $dagcontroleur_parttimedag
}
if (($dag -eq $weekcontrole1) -or ($dag -eq $weekcontrole2))
{$workbook.ActiveSheet.Cells.Item(13,3) = $weekcontrole_persoon}
if ($dag -eq $afwezig_mailboxcontrole)
{$workbook.ActiveSheet.Cells.Item(11,3) = $Controle_mailbox_vrijdag}
$workbook.SaveAs("$huidige_folder\$jaar\$folder_maand\Dagelijks\$datum_vandaag\$Dagelijkse_controle $datum_vandaag")
}
Foreach ($i in $path_dagelijkse_controle)
{
Dagcontrole_folders_maken $i
Foreach ($a in $list_excels)
{Aanpassen_excel_dagcontrole $a $i}
}
# Clean-Up
$Excel.Quit()
Remove-Variable -Name excel
[gc]::collect()
[gc]::WaitForPendingFinalizers()
Making this change should eliminate your concern here...
I'm getting errors on the following part:
$workbook.SaveAs("$huidige_folder$date_today"). Telling it does not
have access to the file.
... because this indicated, the file is still in use. You should always check for the file open/close before processing the next. Since OS and App processing speeds impact availability. AS well as check for the existence of folder or files before you try to use them.
So, modify your code to include error handling, i.e, if/then, try/catch, test-Path, etc.
Simple example check:
# File in use check
$ExcelFilePath = 'D:\Temp\FileData.xlsx'
try {[IO.File]::OpenWrite($ExcelFilePath).close()}
catch {Write-Warning -Message "$ExcelFilePath is in use by another process"}
# Results
<#
WARNING: D:\Temp\FileData.xlsx is in use by another process
#>
I ended up doing the following instead of ForEach in Foreach:
Foreach ($i in $path_dagelijkse_controle) {
Dagcontrole_folders_maken $i
}
foreach($i in 0..2){
Aanpassen_excel_dagcontrole $list_excels[$i] $path_dagelijkse_controle[$i]
Start-Sleep -s 15
}
The sleep function is there because i got a alot of random excel error, on random iterations. And this miraculouslyfixed the error.