Author Posts

May 19, 2015 at 8:44 am

I've got a number of directories to search that contain large files (backups) and small files (md5's).

I've written a script that displays the files in a way that enables me to quickly validate the backups are current and there is an associated md5 for the backup.

As a further script refinement I'd like to modify the file size display so rather than displaying the number of bytes in the large files the size is displayed as xx Gb, xx Mb, xx Kb as appropriate. In other words the value displayed in the file size column would be formatted differently depending on the size of the file.

The following works to display file size in bytes, Kb, Mb, or Gb, depending which is selected, but is applied to all file sizes. I have been unable to create conditional logic to format the display dependent on file size.

@{E={'{0,7:N0} bytes' -f ($_.Length)}}
@{E={'{0,7:N0} Kb   ' -f ($_.Length/1kb)}}
@{E={'{0,7:N0} Mb   ' -f ($_.Length)/1mb}}
@{E={'{0,7:N0} Gb   ' -f ($_.Length/1gb)}}

The current script is as follows. I believe I need to modify a Property of the Select-Object but have been unable to figure out how to make a conditional format property. Any help would be greatly appreciated.

# Call this script from directory where backup files are being checked for current dates.
# The script takes the 20 most recent spi and md5 files in the directory, groups them 
# by drive and sorts in descending order by increment number and age
#
Get-ChildItem | `
 Where-Object {$_.name -like "*spi" -or $_.name -like "*md5"} | `
 Sort-Object LastWriteTime -Descending | `
 Select-Object -First 20 `
  -Property @{L='Drv';E={($_.Name.substring(0,1))}}, Name, LastWriteTime, @{L='Size';E={ '{0,14:N0}' -f ($_.Length)}} | `
 Sort-Object @{E='Drv'}, @{E={($_.Name.substring(12,$_.Name.Indexof(".")-12))};Descending=$true}, @{E='LastWriteTime';Descending=$true} | `
 ft -AutoSize -GroupBy Drv

Sample output from the above script is below.

PS G:\Backup2\Cust1\Dev1> G:\inspect_dir.ps1

   Drv: C

Drv Name                       LastWriteTime         Size
--- ----                       -------------         ----
C   C_VOL-b003-i9107-cd.md5    5/19/2015 12:02:22 AM             59
C   C_VOL-b003-i9107-cd.spi    5/18/2015 11:32:13 PM     99,440,640
C   C_VOL-b003-i9060-cd.md5    5/18/2015 12:04:49 AM             59
C   C_VOL-b003-i9060-cd.spi    5/17/2015 11:31:08 PM     98,367,488
C   C_VOL-b003-i9013-cd-cw.md5 5/18/2015 12:04:10 AM             62
C   C_VOL-b003-i9013-cd-cw.spi 5/16/2015 11:31:59 PM    125,943,296
C   C_VOL-b003-i9013-cd.md5    5/18/2015 12:03:14 AM             59
C   C_VOL-b003-i9013-cd.spi    5/16/2015 11:31:59 PM     98,432,000

   Drv: D

Drv Name                       LastWriteTime         Size
--- ----                       -------------         ----
D   D_VOL-b003-i9104-cd.md5    5/19/2015 12:03:02 AM             59
D   D_VOL-b003-i9104-cd.spi    5/18/2015 11:31:18 PM      5,874,688
D   D_VOL-b003-i9057-cd.md5    5/18/2015 12:06:31 AM             59
D   D_VOL-b003-i9057-cd.spi    5/17/2015 11:31:04 PM    158,806,528
D   D_VOL-b003-i9010-cd-cw.md5 5/18/2015 12:05:56 AM             62
D   D_VOL-b003-i9010-cd.md5    5/18/2015 12:05:24 AM             59

   Drv: E

Drv Name                       LastWriteTime         Size
--- ----                       -------------         ----
E   E_VOL-b003-i9109-cd.md5    5/19/2015 12:02:41 AM             59
E   E_VOL-b003-i9109-cd.spi    5/18/2015 11:30:34 PM      1,001,984
E   E_VOL-b003-i9062-cd.md5    5/18/2015 12:05:11 AM             59
E   E_VOL-b003-i9062-cd.spi    5/17/2015 11:30:31 PM      1,013,248
E   E_VOL-b003-i9015-cd-cw.md5 5/18/2015 12:05:04 AM             62
E   E_VOL-b003-i9015-cd.md5    5/18/2015 12:05:00 AM             59

May 19, 2015 at 9:42 am

First, a couple of general items, you don't need line continuation characters, Powershell see the pipe as a continuation and will ignore whitespace. Second, I don't know what your trying to parse here:

@{E={($_.Name.substring(12,$_.Name.Indexof(".")-12))};Descending=$true}

but I think you are going to have better luck setting it as a calculated property in your Select and then sorting on it. Next, I have two files in C:\ and "Size" is nothing right now:

   Drv: I

Drv Name           LastWriteTime       Length Size          
--- ----           -------------       ------ ----          
I   IFRToolLog.txt 5/1/2015 4:46:31 PM   7788          7,788


   Drv: p

Drv Name        LastWriteTime         Length Size          
--- ----        -------------         ------ ----          
p   pwruser.ini 4/16/2015 10:09:11 AM   4096          4,096

I modified your code to look like this:

Get-ChildItem C:\ -File |
Select-Object -First 20 -Property @{L='Drv';E={($_.Name.substring(0,1))}},
                                  Name,
                                  LastWriteTime,
                                  Length, 
                                  @{L='Size';E={if ($_.Length -gt 5000){"Do GB"}else{"Do MB"} }} | 
Sort-Object -Property @{Expression='Drv'; Ascending=$true }, @{Expression='LastWriteTime';Descending=$true} |
ft -AutoSize -GroupBy Drv

Note that in the Expression (E) that curly braces are executing code. In the curly braces I placed an if that if the length in bytes is bigger than 5000 to write a different message, but you could place a string format there to convert to MB/GB/TB or whatever. You can place try\catch, If, switch, etc. there since you are just executing a code block. Below you will see when this is executed that the first entry is "Do GB" because it is over 5000 bytes.

   Drv: I

Drv Name           LastWriteTime       Length Size 
--- ----           -------------       ------ ---- 
I   IFRToolLog.txt 5/1/2015 4:46:31 PM   7788 Do GB


   Drv: p

Drv Name        LastWriteTime         Length Size 
--- ----        -------------         ------ ---- 
p   pwruser.ini 4/16/2015 10:09:11 AM   4096 Do MB

Not sure what or if there are "best practices" for what you put in those expressions for calculated properties, but I would keep them simple.

May 19, 2015 at 10:57 am

Rob, thanks for your reply. Will look through and see if I understand and can apply your suggestions.

In regard to "...don't know what you're trying to parse..."

The file names are auto generated by the backup with a fixed format. The last part includes a sequence # and indicator of the type of file. Type may be hourly, hourlies consolidated into dailies, dailies into weeklies and weeklies into monthlies. For whatever reason the the sequence number of a daily, weekly or monthly is the same as number used by the last file used to generate it. And, another mystery, the date time stamp of a consolidated file is not always later than the date time stamp of the files it is generated from.

The parsing ensures the backup file and associated md5 are always grouped together in the output regardless of whether there are otherwise duplicate names or the time stamps are not in sequence with the sequence #.

You can see an example of this in the name and time stamp output below

C_VOL-b003-i9013-cd-cw.md5 5/18/2015 12:04:10 AM
C_VOL-b003-i9013-cd-cw.spi 5/16/2015 11:31:59 PM
C_VOL-b003-i9013-cd.md5    5/18/2015 12:03:14 AM
C_VOL-b003-i9013-cd.spi    5/16/2015 11:31:59 PM

May 19, 2015 at 12:57 pm

Here's my adaptation of what you've suggested Rob. It works, thank you. Thought I was doing this earlier but obviously not or it would have worked.

Get-ChildItem | 
 Where-Object {$_.name -like "*spi" -or $_.name -like "*md5"} | 
 Sort-Object LastWriteTime -Descending | 
 Select-Object -First 20 `
  -Property @{L='Drv';E={($_.Name.substring(0,1))}}, `
            Name, `
            LastWriteTime, `
            @{L='Size';E={if ($_.Length -ge 1048576){'{0,14:N0} Mb   ' -f ($_.Length/1mb)}else{'{0,14:N0} bytes' -f ($_.Length)} }} |
 Sort-Object @{E='Drv'}, @{E={($_.Name.substring(12,$_.Name.Indexof(".")-12))};Descending=$true}, @{E='LastWriteTime';Descending=$true} | `
 ft -AutoSize -GroupBy Drv

May 20, 2015 at 6:40 am

...and here's the 'final' version. Note that I found the line continuation characters were needed if the last character is not the pipe.

Get-ChildItem | 
 Where-Object {$_.name -like "*spi" -or $_.name -like "*md5"} | 
 Sort-Object LastWriteTime -Descending | 
 Select-Object -First 20 `
  -Property @{L='Drv';E={($_.Name.substring(0,1))}}, `
            Name, `
            LastWriteTime, `
            @{L='Size';E={if ($_.Length -ge 1gb) {'{0,9:N2} Gb   ' -f ($_.Length/1gb)} `
              elseif ($_.Length -ge 1mb) {'{0,9:N2} Mb   ' -f ($_.Length/1mb)} `
              elseif ($_.Length -ge 1kb) {'{0,9:N2} Kb   ' -f ($_.Length/1kb)} `
              else{'{0,9:N0} bytes' -f ($_.Length)} }} |
 Sort-Object Drv, @{E={($_.Name.substring(12,$_.Name.Indexof(".")-12))};Descending=$true}, `
           @{E='LastWriteTime';Descending=$true} | 
 ft -AutoSize -GroupBy Drv

Thanks again for your help

May 20, 2015 at 9:50 pm

I'm not sure if this will be helpful or not, but this thread inspired me to write a post. If you check it out (link below), you'll see that I used a switch statement inside the Expression section of a calculated property. This allowed me to check each file's size (length), determine if it should be labeled with B, KB, MB, or GB, and then write that out with the modified value (divide by 1 MB, etc. depending). Thanks for the inspiration. I'll just leave this right here: [url]http://tommymaynard.com/script-sharing-return-file-sizes-in-bytes-kb-mb-and-gbs-at-the-same-time-2015/[/url].

May 21, 2015 at 5:13 am

Great post!! Thanks for posting the link.