Link to home
Start Free TrialLog in
Avatar of Thor2923
Thor2923Flag for United States of America

asked on

need to modify my powershell script to display items instead of remove items that are older than 60 days

A few weeks ago I got some excellent advice on how to write a script to remove items recursively from a path on one of our servers. The script ended up looking like this;

$Path = "D:\WSS_Files\data_realtime_archive\DataVolume\Farm(PTI-SQL#SHAREPOINT_CONFIG_FARM1)\http#80#processing\#"
gci $path -filter *.dat -recurse | ?{$_.lastwritetime -lt ((get-date).adddays(-60))} | Remove-Item -recurse -force -whatif

i am hoping now that I can run this same script to only Display items that are older then 60 days. As always I am very cautious when running scripts on my servers. It is as simple as changing the "remove-item" command?


Also, it looks like I will be writing more powershell scripts, do you have any books or course material you would recommend? I have been googling around online, but I would probably do better if I went through some structured material...thanks
Avatar of prashanthd
prashanthd
Flag of India image

Try the following..

Powershell CBT Nuggets are good place to start.
$Path = "D:\WSS_Files\data_realtime_archive\DataVolume\Farm(PTI-SQL#SHAREPOINT_CONFIG_FARM1)\http#80#processing\#"
gci $path -filter *.dat -recurse | where {$_.lastwritetime -lt ((get-date).adddays(-60))} | select fullname,lastwritetime

Open in new window

Avatar of chrismerritt
chrismerritt

Honestly for learning powershell I find the best way to learn is by doing, and answering questions here is good practice too in the PowerShell zone. If I get stuck on something I just google it for a bit :D

You should install Notepad ++ and use that, it makes coding powershell much easier. you can go into Notepad ++ and choose the menu option Language --> P --> PowerShell and it puts nice colors on your text etc, makes it easier to see what is going on.

First hurdle I got over which helped me was learning to write multiline scripts. I started out tentatively running single line/pipeline commands such as:

Get-Mailbox -Server "Some Server" | Select PrimarySMTPAddress

Open in new window


But then I discovered a more interesting way to do this, which means that this can be written as:

$Mailboxes = Get-Mailbox -Server "Some Server"

foreach ($Mailbox in $Mailboxes)
{
	[string]$Mailbox.PrimarySMTPAddress
}

Open in new window


Both do the same thing but the second gives you room to add more lines/processing as you want. The pipeline I find is a bit more restrictive, but can be faster sometimes as it runs asynchronously where the second command does not.

I actually found this resource (online book) to be pretty awesome, a lot of stuff in here I probably don't know about and i've been using PowerShell for a while:

http://powershell.com/cs/blogs/ebook/
Avatar of Thor2923

ASKER

$Path = "D:\WSS_Files\data_realtime_archive\DataVolume\Farm(PTI-SQL#SHAREPOINT_CONFIG_FARM1)\http#80#processing\#"
gci $path -filter *.dat -recurse | where {$_.lastwritetime -lt ((get-date).adddays(-60))} | select fullname,lastwritetime

this script works nice....but I just realized I really need to find out how many files are over 60 days old not just to have them displayed...do you know if there is an easy tweak or extra command I can add to get me a total count of old files at the end or would that require writing a totally different script?
This should do it:

$Path = "D:\WSS_Files\data_realtime_archive\DataVolume\Farm(PTI-SQL#SHAREPOINT_CONFIG_FARM1)\http#80#processing\#"
$Files = gci $path -filter *.dat -recurse | where {$_.lastwritetime -lt ((get-date).adddays(-60))} | select fullname,lastwritetime
write-host "File Count: $(Files.Count)"
Start-Sleep 5
$Files | Out-Host

Open in new window


Added a sleep for 5 seconds after it echos back the count before it puts the files into the host output. Change this is you want to though.
I ran this is my test environment by just copying and pasting your script. I got an error "the term FILES.COUNT is not recognized as a cmdlet, function or operable program or script file"

Is there possible a typo?? I am reviewing myself and all the syntax looks correct, but as you know my scripting experiance is limited
ASKER CERTIFIED SOLUTION
Avatar of chrismerritt
chrismerritt

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
well that worked perfectly in my test environment....the file count was 50 and all the files displayed below, but when I ran it live there were so many files I could not see the beginning where the file count was generated....is there a way to have the file count come out at the bottom??
Just shift stuff about if you want, but this will do it:

$Path = "D:\WSS_Files\data_realtime_archive\DataVolume\Farm(PTI-SQL#SHAREPOINT_CONFIG_FARM1)\http#80#processing\#"
$Files = gci $path -filter *.dat -recurse | where {$_.lastwritetime -lt ((get-date).adddays(-60))} | select fullname,lastwritetime
$Files | Out-Host
write-host "File Count: $($Files.Count)"

Open in new window

ok, I will play around, thanks for all the new toys and ideas!
is files.count an actualy powershell command or just a variable you made up? I am trying to understand how the script runs exactly.

when you wrote "| select fullname, lastwritetime"

is "fullname" a powershell command also? I do not see anywhere that you tell the script the output is to be called fullname

ok in reading more, it looks like by "select fullname, lastwritetime" you are declaring that fullname will be the output file

I just do not see where the script knows the output is being sent to "$files.count"
Let me step through the script in a bit more detail for you line by line.

Be warned this is long, though I hope you will stick with it :)

1. Specify the path you want to work with:

$Path = "D:\WSS_Files\data_realtime_archive\DataVolume\Farm(PTI-SQL#SHAREPOINT_CONFIG_FARM1)\http#80#processing\#"

Open in new window


All this line is doing is saying that the variable $Path is now equal to the folder path within the quotes. PowerShell is quite smart and generally knows that anything in the quotes is a string variable.

As in most programming systems, quotes are used to enclose strings, and you can put numbers without quotes to store them as numbers for example.

if you put the above into PowerShell and echo it back it will list you the string you just entered:

PS C:\Users\Chris> $Path = "D:\WSS_Files\data_realtime_archive\DataVolume\Farm(PTI-SQL#SHAREPOINT_CONFIG_FARM1)\http#80#processing\#"
PS C:\Users\Chris> $Path
D:\WSS_Files\data_realtime_archive\DataVolume\Farm(PTI-SQL#SHAREPOINT_CONFIG_FARM1)\http#80#processing\#
PS C:\Users\Chris>

2. Get collection of files:

$Files = gci $path -filter *.dat -recurse | where {$_.lastwritetime -lt ((get-date).adddays(-60))} | select fullname, lastwritetime

Open in new window


This line is saying that the variable $Files is equal to everything that occurs after the = sign.

gci is shorthand for Get-ChildItem, which you can use to get content in a folder in Windows. Put some files into a folder for example C:\TEMP and then run this to see what I mean:

[Excercise]
Get-ChildItem "C:\TEMP\"

Running gci against the $Path variable will get all the files etc in the folder you specified in line 1.

Using the -filter variable with *.dat means that it will only return files with the extension .dat

Using the -recurse switch means it will scan all folders/subfolders in the $Path variable instead of just the single folder level.

Piping this into "where {$_.lastwritetime -lt ((get-date).adddays(-60))}" will get all files where the lastwritetime property is less than the current date -60 days. Though if you wanted 2 months you could instead say "AddMonths(-2)" instead of ".AddDays(-60)".

In the next pipeline section, saying "select fullname, lastwritetime" means that it will only return the FullName (i.e. the file path) and the lastwritetime properties from all of the files.

Try this to see what I mean:

[Excercise]
Get-Service

Returns the list of Services on your machine, what if you only want the Name and the Status fields? easy :)

Get-Service | Select Name, Status

3. Show the files found in the previous line on the host screen.

$Files | Out-Host

Open in new window


This command basically says echo the $Files list back to the host screen. Piping to Out-Host is a good way to force an output onto the screen.

You will find if you simply type the variable name into the window it will come back as well, i.e. you can just type $Files and it will echo back the same thing.

I find that Out-Host is better practice though, and is indeed necessary if you want to force an echo to host in something like a Function.

4. Echo back file count.

The $Files variable we made by getting a collection of files that were older than 60 days is stored in an Array or Collection of items, each file is a seperate line or item.

You can see this by putting $Files[0] into the window to echo back the first entry in the Array (Arrays start at 0, so n - 1 to get the item you want back. If you want the 5th item you would need the [4]th item in the Array list). $Files[1] gets back the 2nd file, $Files[2] the 3rd etc.

write-host "File Count: $($Files.Count)"

Open in new window


This line echos back the "Count" method of the Array to see how many items it contains.

[Excercise]
Type the following into PowerShell after running the whole original script:

Get-Member -InputObject $Files

You should see some properties that belong to the array. The Count property is the one I took advantage of.

write-host writes something to the host. Yes I know this is similar to the Out-Host method and in fact they can both be used in a similar way. Though write-host lets you do some cool stuff like changing the colours.

$Files.Count is what I wanted to get back. the .Count bit just says get back the "Count" property of the $Files object.

the $() around the variable just makes sure it expands the variable properly in the string.

Take this for example:

[Excercise]
$Service = Get-Service | Where {$_.DisplayName -eq "Task Scheduler"}

Now when you want to write something about this in a sentence you will see why I did what I did:

"The service $Service.DisplayName is currently $Service.Status"

Comes out as:

The service System.ServiceProcess.ServiceController.DisplayName is currently System.ServiceProcess.ServiceController.Status

This one:

"The service $($Service.DisplayName) is currently $($Service.Status)"

Comes out as:

The service Task Scheduler is currently Running

The second one is right in the context of writing about the service in a string.
Avatar of Brent Challis
There is a cmdlet designed for counting things, Measure-Object.  If you pipe a collection to Measure-Object it will tel you how many objects there are in the collection.  It can also provide some basic statistical analysis as well.  Here is a reference:

http://technet.microsoft.com/en-us/library/ee176900.aspx

Besides Notepad++, there is a free editor called PowerGUI (http://powergui.org/index.jspa).  I also use the PowerShell_ISE that installs with PowerShell 2.0 (It is what I use for the Microsoft PowerShell course) and also use Sapiens PrimalScript (which is a commercial product.)

I agree that the best way to learn is by doing.  

There are many on-line and printed resourses available.  If you were to buy one book, I would recommend PowerShell in Practice by Richard Siddaway (http://www.amazon.com/PowerShell-Practice-Richard-Siddaway/dp/1935182005/ref=sr_1_1?ie=UTF8&qid=1318453123&sr=8-1) and as a useful reference to have next to your keyboard, http://refcardz.dzone.com/refcardz/windows-powershell.

Besides the references, though, your best support will be the cmdlets Get-Help, Get-Command and Get-Member.  These will help you explore what powerhell offers.  There is an overview of these here: http://www.ddls.net.au/blog/2011/08/powershell-1-2-3/

I also use a variation on Get-Command to find commands that support particular parameters:
http://www.ddls.net.au/blog/2011/09/get-commandbyparameter/

To repeat, the best way to learn is by doing, and in particular to work with  scripts when you are not up against a tight deadline.
thanks you have been great and taught me a lot....if there is anything I can ever do for you feel free to msg me