Remove 2nd level Sub Directories - powershell-2.0

The script below finds folders in the E:\WebDelivery\MagicOS directory. If any of the folders in this directory are older than 14 days it will then delete them and their sub-directories. It excludes files that are named upd.info.raf.
I need to change the script to only look in the sub-directory for the modified date and if that date is older than 14 days, it needs to delete those folders and their contents but leave the S6.15, T7.12 type parent directory alone.
Here is the folder structure:
E:\WebDelivery\MagicOS ->> under this are the directories S6.15, T7.12, S5.6 etc. and under each of those there are directories all with numbers as names 1, 2,3 etc and possibly a file called upd.info.raf
E\WebDelivery\MagicOS ->> S6.15
->> 1
->> 2
Currently the script is checking the S6.15 level folders for the modified date and removing the folder and it's sub-directories if they are older than 14 days. I need instead to check the number sub directories 1, 2, 3 etc for modified dates and remove those if they are older then 14 days but leave the parent S6.15 directory. Basically I don't care about the date on the S6.15 folder only the number sub folders 1, 2, 3 etc...
This is where I'm stumped. Any ideas would be helpful. Here is the existing script.
$Today = Get-Date
$DaysToKeep = 14
$TargetFolder = "E:\WebDelivery\MagicOS"
$LastWrite = $Today.AddDays(-$DaysToKeep)
$excludeFolders = #("testdir","testdir2")
$Folders = Get-Childitem –path $Targetfolder |
Where {$_.psIsContainer} |
Where {$_.LastWriteTime -le "$LastWrite"} |
Where {$excludeFolders -notcontains $_.Name}
If ($Folders) {
foreach ($Folder in $Folders) {
Write-Host "Processing Folder $($Folder.Name)"
$items = Get-ChildItem -Path $Folder.FullName -Exclude
"upd.info.raf" -Recurse
If ($items) {
Write-Host "Removing: $($Folder.Name)"
# $items | Remove-Item -Recurse -Verbose
} Else {
Write-Host "There are no items to remove in $($Folder.Name)"
}
}
} Else {
Write-Host "There are no folders to empty."
}

I actually was able to figure this out on my own. I added a foreach loop to process each child-item of the sub directory and remove the old folders.
Here is the modified script:
$Today = Get-Date
$DaysToKeep = 14
$TargetFolder = "E:\WebDelivery\testdir"
$LastWrite = $Today.AddDays(-$DaysToKeep)
$excludeFolders = #("testdir","testdir2")
$Folders = Get-Childitem –path $Targetfolder |
Where {$_.psIsContainer} |
Where {$excludeFolders -notcontains $_.Name}
If ($Folders) {
foreach ($Folder in $Folders) {
Write-Host "Processing Folder $($Folder.Name)"
$items = Get-ChildItem -Path $Folder.FullName -Exclude
"upd.info.raf" | Where {$_.LastWriteTime -le "$LastWrite"}
If ($items) {
foreach ($item in $items){
Write-Host "Removing: $($item.FullName)"
$item | Remove-Item -Recurse -Verbose
}
} Else {
Write-Host "There are no items to remove in $($Folder.Name)"
}
}
} Else {
Write-Host "There are no folders to empty."

Related

Split EDI X12 files using Powershell

I am likely recreating the wheel here but this is my stab and solving an issue partly and asking for community assistance to resolve the remaining.
My task is to split EDI X12 documents into their own file (ISA to IEA)
and CRLF each line separately (similar to ex. EDI2.EDI below).
Below is my Powershell script and example EDI documents 1, 2 and 3.
My script will successfully split a contiguous X12 EDI document from ISA to IEA and CRLF into a file so that one contiguous string becomes something more readable. This works well and will even handle any segment delimiter as well as any line delimiter.
My issue is dealing with non-contiguous documents (ex. EDI2) or combined (ex. EDI3). The source folder could have any of the formatted files shown below. If the file already contains the CRLF, then I just need to split it from ISA to IEA. My script is failing when i pull in CRLF'd files.
Could someone help me solving this?
$sourceDir = "Z:\temp\EDI\temp\"
$targetDir = "Z:\temp\EDI\temp\archive"
<##### F U N C T I O N S #####>
<#############################>
Function FindNewFile
{
Param (
[Parameter(mandatory=$true)]
[string]$filename,
[int]$counter)
$filename = Resolve-Path $filename
$validFileName = "{0}\{1} {2}{3}" -f $targetDir, #([system.io.fileinfo]$filename).DirectoryName,
([system.io.fileinfo]$filename).basename,
$counter, #"1", #([guid]::newguid()).tostring("N"),
([system.io.fileinfo]$filename).extension
Return $validFileName
}
<###### M A I N L I N E ######>
<#############################>
If(test-path $sourceDir)
{
$files = #(Get-ChildItem $sourceDir | Where {!$_.PsIsContainer -and $_.extension -eq ".edi" -and $_.length -gt 0})
"{0} files to process. . ." -f $files.count
If($files)
{
If(!(test-path $targetDir))
{
New-Item $targetDir -ItemType Directory | Out-Null
}
foreach ($file in $files)
{
$me = $file.fullname
# Get the new file name
$isaCount = 1
$newFile = FindNewFile $me $isaCount
$data = get-content $me
# Reset variables for each new file
$dataLen = [int] $data.length
$linDelim = $null
$textLine = $null
$firstRun = $True
$errorFlag = $False
for($x=0; $x -lt $data.length; $x++)
{
$textLine = $data.substring($x, $dataLen)
$findISA = "ISA{0}" -f $textLine.substring(3,1)
If($textLine.substring(0,4) -eq $findISA)
{
$linDelim = $textLine.substring(105, 1)
If(!($FirstRun))
{
$isaCount++
$newFile = FindNewFile $me $isaCount
}
$FirstRun = $False
}
If($linDelim)
{
$delimI = $textLine.IndexOf($linDelim) + 1
$textLine = $textLine.substring(0,$delimI)
$fLine = $textLine
add-content $newFile $fLine
$x += $fLine.length - 1
$dataLen = $data.length - ($x + 1)
}
Else
{
$errorFlag = $True
"`t=====> {0} is not a valid EDI X12 file!" -f $me
$x += $data.length
}
}
If(!($errorFlag))
{
"{0} contained {1} ISA's" -f $me, $isaCount
}
}
}
Else
{
"No files in {0}." -f $sourceDir
}
}
Else
{
"{0} does not exist!" -f $sourceDir
}
Filename: EDI1.EDI
ISA*00* *00* *08*925xxxxxx0 *01*78xxxx100 *170331*1630*U*00401*000000114*0*P*>~GS*FA*8473293489*782702100*20170331*1630*42*T*004010UCS~ST*997*116303723~SE*6*116303723~GE*1*42~IEA*1*000000114~ISA*00* *00* *08*WARxxxxxx *01*78xxxxxx0 *170331*1545*U*00401*000002408*0*T*>~GS*FA*5035816100*782702100*20170331*1545*1331*T*004010UCS~ST*997*000001331~~SE*24*000001331~GE*1*1331~IEA*1*000002408~
Filename: EDI2.EDI
ISA*00* *00* *ZZ*REINxxxxxxxDSER*01*78xxxx100 *170404*0819*|*00501*100000097*0*P*}~
GS*PO*REINHxxxxxxDSER*782702100*20170404*0819*1097*X*005010~
ST*850*1097~
SE*14*1097~
GE*1*1097~
IEA*1*100000097~
Filename: EDI3.EDI
ISA*00* *00* *08*925xxxxxx0 *01*78xxxx100 *170331*1630*U*00401*000000114*0*P*>~GS*FA*8473293489*782702100*20170331*1630*42*T*004010UCS~ST*997*116303723~SE*6*116303723~GE*1*42~IEA*1*000000114~ISA*00* *00* *08*WARxxxxxx *01*78xxxxxx0 *170331*1545*U*00401*000002408*0*T*>~GS*FA*5035816100*782702100*20170331*1545*1331*T*004010UCS~ST*997*000001331~~SE*24*000001331~GE*1*1331~IEA*1*000002408~
ISA*00* *00* *ZZ*REINxxxxxxxDSER*01*78xxxx100 *170404*0819*|*00501*100000097*0*P*}~
GS*PO*REINHxxxxxxDSER*78xxxxxx0*20170404*0819*1097*X*005010~
ST*850*1097~
SE*14*1097~
GE*1*1097~
IEA*1*100000097~
FWIW, I've compiled this code from all over the net including stackoverflow.com. If you see your code and desire recognition, let me know and I'll add it. I'm not claiming any of this is original! My motto is "ARRRGH!"
EDI3 is an invalid X12 document, each file should only contain one ISA segment with repeated envelopes if required.
The segment terminator should also be consistent. In EDI3 it is both ~ and ~ which is again invalid.
Segment terminator should be tilde "~".
It can be suffixed by: nothing, "\n" or, "\r\n", what is optional is the suffix for human reading. Some implementations might be more relaxed in terms of the X12 standard.
https://www.ibm.com/support/knowledgecenter/en/SS6V3G_5.3.1/com.ibm.help.gswformstutscreen.doc/GSW_EDI_Delimiters.html
https://docs.oracle.com/cd/E19398-01/820-1275/agdbj/index.html
https://support.microsoft.com/en-sg/help/2723596/biztalk-2010-configuring-segment-terminator-for-an-x12-encoded-interch
BTW, check my splitter/viewer: https://gist.github.com/ppazos/94a63ab18910ab0c0d23c9ff4ff7e5c2

ProgressBar Overlay Or ProgressBar from Copy Functions

Function GetTotalBytesOfCopyDestination{
param($destinationPath);
$colItems = (Get-ChildItem $destinationPath | Measure-Object -property length -sum)
return $colItems.sum;
}
Function GetBytesOfFile{
param($sourcePath);
return (Get-Item $sourcePath).length;
}
Function GetPosition{
param([double]$currentOfBytesSended)
param([double]$countsOfBytesWillSend)
$position = ($currentOfBytesSended / $countsOfBytesWillSend) * 100;
#range 0 - 100
#(15800 bytes / 1975633689 bytes)*100
#
return $position;
}
Function Copy-File {
#.Synopsis
# Copies all files and folders in $source folder to $destination folder, but with .copy inserted before the extension if the file already exists
param($source,$Destination2)
# create destination if it's not there ...
mkdir $Destination2 -force -erroraction SilentlyContinue
[double]$currentOfBytesSended = 0;
[double]$countsOfBytesWillSend = 0;
[double]$countsOfBytesWillSend = GetTotalBytesOfCopyDestination($source);
$progressbar6.Maximum = 100;
$progressbar6.Step = 1;
foreach($original in ls $source -recurse) {
$result = $original.FullName.Replace($source,$Destination2)
while(test-path $result -type leaf){ $result = [IO.Path]::ChangeExtension($result,"copy$([IO.Path]::GetExtension($result))") }
[System.Windows.Forms.Application]::DoEvents()
if($original.PSIsContainer) {
mkdir $result -ErrorAction SilentlyContinue
} else {
copy $original.FullName -destination $result
[System.Windows.Forms.Application]::DoEvents()
$currentCopyingFileSizeInBytes = 0;
$currentCopyingFileSizeInBytes = GetBytesOfFile($original.FullName);
$currentOfBytesSended = [double]$currentOfBytesSended + [double]$currentCopyingFileSizeInBytes;
#$currentOfBytesSended += $currentCopyingFileSizeInBytes;
$progressbar6.Value=GetPosition([double]$currentOfBytesSended, [double]$countsOfBytesWillSend);
[System.Windows.Forms.Application]::DoEvents()
#$progressbar6.PerformStep();
$progressbar6.Refresh();
}
}
}
what I'm trying to get is Copy-File Function ,copy files & directory from remote machine to local machine while moving progress bar depending on total amount to copy,and which are already copied and it define position for the progress bar and i get this error
ERROR: GetPosition : Cannot process argument transformation on parameter 'currentOfBytesSended'. Cannot convert the "System.Object[]" value of type "System.Object[]"
ERROR: to type "System.Double".
Assit App.pff (337): ERROR: At Line: 337 char: 35
ERROR: + $progressbar6.Value=GetPosition([double]$currentOfBytesSended, [double]$coun ...
ERROR: + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ERROR: + CategoryInfo : InvalidData: (:) [GetPosition], ParameterBindingArgumentTransformationException
ERROR: + FullyQualifiedErrorId : ParameterArgumentTransformationError,GetPosition
ERROR:
>> Script Ended
First of all, welcome to StackOverflow. Try to break up your questions into individual questions, rather than grouping many of them together. The format of this site is better suited to one question, one answer, unless maybe there is guidance that states otherwise.
Excluding Folders
You can ignore folders by using the Exclude parameter on the Get-ChildItem command.
Imagine that you have a folder structure similar to the following:
c:\gci
|
|\a
| \a.txt
|\b
| \b.txt
|\c
| \c.txt
If you only want to get the contents of c:\gci\a and c:\gci\c, you can exclude c:\gci\b using the command:
Get-ChildItem -Path c:\gci -Exclude b* -Recurse;
Keep in mind that this will also exclude other items starting with "b" such as c:\gci\bcd.txt.
Progress Bar
You can create a progress bar using the Write-Progress command. You will have to write your own, custom logic to determine what tasks to report progress on, and what the percentage of progress is. You can do this based on the number of bytes copied vs. the total number of bytes to be copied, the number of files copied vs. the total number of files, or some other sort of metric.
There's no simple answer to this one. You will have to perform the calculations yourself, and call Write-Progress at the appropriate times.

Powershell 2.0 Get users for a local group

How can I get user list from a local group? I only have PS 2.0 and it does not have Get-ADGroup command.
I can get local groups:
$adsi = [ADSI]"WinNT://$env:COMPUTERNAME"
$groups = $adsi.Children | Where { $_.SchemaClassName -eq 'Group' }
$group | ft Name
What I need is to list all the members for each group.
You can try the following
$obj = [ADSI]"WinNT://$env:COMPUTERNAME"
$admingroup = $obj.Children | Where { $_.SchemaClassName -eq 'group'} | where {$_.name -eq 'Administrators'}
$admingroup.Invoke('Members') | % {$_.GetType().InvokeMember('Name', 'GetProperty', $null, $_, $null)}
$admingroup.Invoke('Members') | % {$_.GetType().InvokeMember('Name', 'GetProperty', $null, $_, $null)}
Here are the common properties
String :
Description, FullName, HomeDirectory, HomeDirDrive, Profile, LoginScript, ObjectSID
Integer :
UserFlags, PasswordExpired, PrimaryGroupID
Time :
PasswordAge
You'll find more in Microsoft documentation.
Try this
$computer = [ADSI]"WinNT://$env:COMPUTERNAME"
$computer.psbase.children | where { $_.psbase.schemaClassName -eq 'group' } | foreach {
write-host $_.name
write-host "------"
$group =[ADSI]$_.psbase.Path
$group.psbase.Invoke("Members") | foreach {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)}
write-host
}
This doesn't give the domain though, hence i had to look for other ways, like:
If you want to see members of a local group quickly:
PS C:\> net localgroup USERS
Alias name USERS
Comment Users are prevented from making accidental or intentional system-wide changes and can run most applications
Members
-------------------------------------------------------------------------------
NT AUTHORITY\Authenticated Users
NT AUTHORITY\INTERACTIVE
The command completed successfully.
Now you can manipulate this output a bit to get what you need:
$computer = [ADSI]"WinNT://$env:COMPUTERNAME"
$groups = $computer.psbase.children | where { $_.psbase.schemaClassName -eq 'group' } | select -ExpandProperty Name
Foreach($group in $groups)
{
write-host $group
write-host "------"
net localgroup $group | where {$_ -notmatch "command completed successfully"} | select -skip 6
Write-host
}

exception calling "Parse" with 1 argument: Input string was not in a correct format

I have a script that works properly when run as an administrator but gives a Parse error when run as a normal user. Any Ideas?;
SCRIPT
`NeverExpires = 9223372036854775807;
$ExpireMin = (Get-Date).AddDays(4);
$ExpireMax = (Get-Date).AddDays(9);
$Userlist = Get-ADUser -Filter * -Properties name, samaccountname, accountexpirationdate, enabled, distinguishedname, accountExpires | Where-object {($_.DistinguishedName -notlike "*OU=Terminated,OU=Users,OU=Home Office,DC=Domain,DC=com")} |
Where-Object {$_.accountExpires -ne $NeverExpires `
-and [datetime]::FromFileTime([int64]::Parse($_.accountExpires)) -lt $ExpireMax `
-and [datetime]::FromFileTime([int64]::Parse($_.accountExpires)) -gt $ExpireMin }
$Userlist | select name, samaccountname, accountexpirationdate, enabled, distinguishedname | export-csv $ReportName -notypeinformation
Send-MailMessage -To $To -From $From -Subject $Subject -Body $Body -SMTPServer $SMTPServer -Attachments $ReportName
Get-ADUser -Filter * -Properties accountExpires |
Where-Object {$_.accountExpires -ne $NeverExpires `
-and [datetime]::FromFileTime([int64]::Parse($_.accountExpires)) -lt $ExpireMax `
-and [datetime]::FromFileTime([int64]::Parse($_.accountExpires)) -gt $ExpireMin } | ForEach {
$account = $_
$manager = Get-ADUser -Identity $account -Properties EmailAddress,Manager | %{(Get-AdUser $_.Manager -Properties EmailAddress).EmailAddress}`
I would say that $_.accountExpires is null either because the property could not be retrieved or $_ is itself null. Powershell will quietly convert null to the empty string resulting an invalid format for parsing. Note that the Parse call is completely unnecessary because powershell will automatically try to coerce the string for you and will likely give you a much better error message. Although null will be coerced to 0 as a long.

Using System.Drawing.Image to Export-Csv with $imageFile.Width and $_.Fullname

I wonder if there is a betterr way to write this script to gather image dimensions and filepaths. The script works great on small to medium size directories, but I'm not positive that 100,000+ files/folders is possible.
Measure-Command {
[Void][System.Reflection.Assembly]::LoadFile( "C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Drawing.dll")
$path = "\\servername.corp.company.com\top_directory"
$data = Get-ChildItem -Recurse $path | % {
$imageFile = [System.Drawing.Image]::FromFile($_.FullName) ;
New-Object PSObject -Property #{
name = $_.Name
fullname = $_.Fullname
width = $imageFile.Width
height = $imageFile.Height
length = $_.Length
}
}
$data | Where-Object {$_.width -eq 500 -or $_.width -eq 250 -or $_.width -eq 1250 } |
Export-Csv \\servername.corp.company.com\top_directory\some_directory\log_file.csv -NoTypeInformation }
I don't actually use the Where-Object filter right now.
When running the above script on a remote directory with appx. 20,000 files + folders the script takes appx. 26 minutes, before creating a .csv.
I am running the script from Powershell V2 ISE on Windows 7 and I belive the remote server is on Windows Server 2003.
Would running the script directly from the remote server be faster?
Is the process of exoprting the csv slow since all data is collected in "cache" before being written to the csv?
If all I had to go through was 20,000 files, I'd wait the 26 minutes, but 500,000 files and folders is a long wait.
I'm testing out the below method since I think my real issue is not the speed, but storing too much data in the memory. Thanks to a post from George Howarth for this, and to PoSherLife for the top script -http://powershellcommunity.org/tabid/54/aft/4844/Default.aspx
Measure-Command {
[System.Reflection.Assembly]::LoadFile( "C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Drawing.dll")
"Name|SizeInBytes|Width|Height|FullName" >> C:\Users\fcool\Documents\JPGInfo.txt
$path = "C:\Users\fcool\Documents"
$images = Get-ChildItem -Recurse $path -Include *.jpg
foreach ($image in $images)
{
$name = $image.Name
$length = $image.Length
$imageFile = [System.Drawing.Image]::FromFile($image.FullName)
$width = $imageFile.Width
$height = $imageFile.Height
$FullName = $image.Fullname
"$name|$length|$width|$height|$FullName" >> C:\Users\fcool\Documents\JPGInfo.txt
$imageFile.Dispose()
}
}
Is there any risk/loss of performance when running these scripts on non-image filetypes?
when I don't exclude non-images, I get this error:
Exception calling "FromFile" with "1" argument(s): "Out of memory."
At C:\scripts\directory_contents_IMAGE_DIMS_ALT_method.ps1:13 char:46
+ $imageFile = [System.Drawing.Image]::FromFile <<<< ($image.FullName)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
thanks for any advice!
and thanks again to George Howarth and to PoSherLife for the scripts!
Using -Filter with Get-ChildItem is much faster than -Include however you can only apply one filter string. So if you only want to match *.jpg you can use filter. In my testing using filter was close to 5 times faster than include.
Get-ChildItem -Recurse \\server\Photos -Filter *.jpg | % {
$image = [System.Drawing.Image]::FromFile($_.FullName)
if ($image.width -eq 500 -or $image.width -eq 250 -or $image.width -eq 1250) {
New-Object PSObject -Property #{
name = $_.Name
fullname = $_.Fullname
width = $image.Width
height = $image.Height
length = $_.Length
}
}
} | Export-Csv 'C:\log.csv' -NoTypeInformation

Resources