views:

1566

answers:

5

What is the most compact way of deleting all files from a folder except one file in a PowerShell script. I doesn't matter at all which file is kept, just as long as one is kept.

I'm using the PowerShell 2 CTP.

UPDATE:
An amalgamation of all the answers so far...

$fp = "\\SomeServer\SomeShare\SomeFolder"
gci $fp |where {$_.mode -notmatch "d"} |sort creationtime -desc |select -last ((@(gci $fp)).Length - 1) |del

Anybody see any issues with using this? How about the -notmatch part?

A: 

del . -Exclude (dir | sort creationtime -desc)[0] -whatif

This will delete all files except the most recently created.

Chris Ballance
I think that would leave a folder that is more recent than all the files.
EBGreen
hmmm. It would have to leave a single file. Folders/Subfolders need to stay untouched.
tyndall
+1  A: 

how about:

dir $dirName | select -first ((dir $dirName).Length -1) | del

deletes all but the last one.

Edit: A more flexible version, plus you won't have to enter the dir command twice:

$include=$False; dir $dirNam | where {$include; $include=$True;} | del

Note, this does the opposite, it deletes all but the first. It also allows you to add clauses, such as to not act on directories:

$include=$False; dir $dirNam | where {$include -and $_.GetType() -ne [System.IO.DirectoryInfo]; $include=$True;} | del

Edit 2 with regards to excluding directories using the Mode property. I guess that should work provided that the framework does not change the way the mode string is generated (I can't imagine it will). Though I might tighten up the regular expression to:

$_.Mode -notmatch "^d.{4}"

If you are trying to avoid typing, adding a function to your profile is your best bet:

function isNotDir($file) { return $file.GetType() -ne [System.IO.DirectoryInfo];}
dir $dirName | where {isNotDir($_)}
zdan
How can you specify the path and make it all one line of code?
tyndall
be aware that this one will delete folders as well.
EBGreen
I have edited this with a more flexible solution that will allow you to skip folders.
zdan
+1. I like where this is headed - especially the $_.GetType() -ne [System.IO.DirectoryInfo]. But when I try to use it I get an error (below)
tyndall
You cannot call a method on a null-valued expression. + gci $dirName |where-object $_.GetType <<<< () -ne [System.IO.DirectoryInfo] + CategoryInfo : InvalidOperation: (GetType:String) [], RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull
tyndall
would this work |where-object {$_.mode -notmatch "d"} or will that breakdown sometimes
tyndall
@Bruno Tyndall: I think you forgot the braces for the where-object clause.
zdan
As mentioned in another answer, it would probably be better to use "$_.PSIsContainer". Also, don't forget about the -is operator: $_ -is [System.IO.DirectoryInfo]
JasonMArcher
+4  A: 

Without any built-in functions it is a little bit convoluted because the functions neeed to deal with definitive length. But you can do it this way which involves looking at the directory twice

gci $dirName | select -last ((@(gci $dirName)).Length-1) | del

I wrote several powershell extensions that make tasks like this a lot easier. One example is Skip-Count which allows for an arbitrary number of elements to be skipped in the pipeline. So the code can be quickly searched to only look at the directory once

gci $dirName | skip-count 1 | del

Source to Skip-Count: http://blogs.msdn.com/jaredpar/archive/2009/01/13/linq-like-functions-for-powershell-skip-count.aspx

EDIT

In order to kill folders use "rm -re -fo" instead of "del"

EDIT2

In order to avoid all folders (empty or not) you can modify the code as such

gci $dirName | ?{ -not $_.PSIsContainer } | skip-count 1 | del

The PSISContainer member is only true for folders.

JaredPar
That would still kill folders right?
EBGreen
@EBGreen, no but I added an Edit to specify how to make it kill folders.
JaredPar
JaredPar, what exactly keeps it from deleting folders? See my last comments on zdans answer.
tyndall
@Bruno, by default del will not delete a non-empty folder.
JaredPar
+6  A: 

In PS V2 we added -SKIP to Select so you could do:

dir | where {$_.mode -notmatch "d"} |select -skip 1 |del

Jeffrey Snover - MSFT
Welcome to SO, Jeffrey.
Jay Bazuzi
This looks like it might be a great repository for formalized folklore.
Jeffrey Snover - MSFT
A: 

My favorite:

move file to preserve elsewhere
delete all files
move preserved file back
Joshua