Powershell get second folder in filepath - powershell

I am new to powershell, I am trying to get the second folder in a filepath, after searching folders for a specific function. The issue seems to be when using split-item, i have differing levels of folder depths.
Get-ChildItem -Path d:\domains -Recurse *.php | Select-Object -Property FullName
So for example I am outputting:
D:\domains\domain.com\httpdocs\wordpress\wp-content\themes\twentytwelve\single.php
D:\domains\domain.com\httpdocs\wordpress\wp-content\themes\twentytwelve\tag.php
D:\domains\domain.com\httpdocs\wordpress\wp-includes\canonical.php
D:\domains\domain2.com\httpdocs\wordpress\wp-content\themes\twentytwelve\inc\custom-header.php
D:\domains\domain2.com\httpdocs\wordpress\wp-content\themes\twentytwelve\page-templates\front-page.php
D:\domains\domain2.com\httpdocs\wordpress\wp-content\themes\twentytwelve\page-templates\full-width.php
D:\domains\domain2.com\httpdocs\wordpress\wp-includes\canonical.php
I just want to search for the name of the second folder (i.e. domain.com/domain2.com) and just output the unique entries.
Many Thanks

Use Select-Object -First 1 -Skip 1 to grab the second item in a pipeline:
Get-ChildItem -Directory -Path d:\domains |Select -First 1 -Skip 1 |Get-ChildItem -Recurse *.php |Select-Object -ExpandProperty FullName
The -Directory switch for Get-ChildItem on the file system provider and the -First and -Skip parameters for Select-Object were introduced in PowerShell 3.0.
If you're using PowerShell v2, you could do the following:
Get-ChildItem -Path #(Get-ChildItem -Path D:\domains |Where-Object {$_.PSIsContainer})[1].FullName -Recurse *.php |Select-Object -ExpandProperty FullName

Related

Powershell: how to loop through folders and execute the same code for each of the folders

Currently I have a script that will sort files in a folder (on their lastwritetime), keep the latest file and move the other files to a different folder. This works correctly:
Get-ChildItem "\\networkfolder\RawData\2_ActionData_Prep\CustomerA\" -Recurse -Filter "*.rpt" -File |
Sort-Object -Property LastWriteTime -Descending |
Select-Object -Skip 1 |
Move-Item -Force -Destination "\\networkfolder\RawData\_Archive\Archive_DataRetrieved\"
The problem is that I have several 'customer' folders and I want to execute the code above in each of those folders.
I tried the following:
$CustomerFolders = Get-ChildItem -Path "\\networkfolder\RawData\2_ActionData_Prep\" -Directory -Recurse
foreach ($folder in $CustomerFolders) {Get-ChildItem -Filter "*.rpt" -File | Sort-Object -Property LastWriteTime -Descending |
Select-Object -Skip 1 |
Move-Item -Force -Destination "\\networkfolder\RawData\_Archive\Archive_DataRetrieved\"}
When I execute this script, nothing happens. Also no error comes up. Hopefully someone could help me on this.
Santiago Squarzon noticed that a $folder was missing, so I added $folder in loop for Get-Childitem:
$CustomerFolders = Get-ChildItem -Path "\\networkfolder\RawData\2_ActionData_Prep\" -Directory -Recurse
foreach ($folder in $CustomerFolders) {Get-ChildItem $folder -Filter "*.rpt" -File | Sort-Object -Property LastWriteTime -Descending |
Select-Object -Skip 1 |
Move-Item -Force -Destination "\\networkfolder\RawData\_Archive\Archive_DataRetrieved\"}
Now I get an error message:
Get-ChildItem : Cannot find path '\networkfolder\CustomerA' because it does not exist.
It somehow misses the part \RawData\2_ActionData_Prep\ in the path, although I defined it in the $CustomerFolders variable?
You could do the process all with pipelines like this:
$base = "\\networkfolder\RawData\2_ActionData_Prep\"
$destination = "\\networkfolder\RawData\_Archive\Archive_DataRetrieved\"
Get-ChildItem -Path $base -Directory -Recurse | ForEach-Object {
$_ | Get-ChildItem -Filter "*.rpt" -File | Sort-Object LastWriteTime -Descending |
Select-Object -Skip 1 | Move-Item -Force -Destination $destination
}
To briefly explain why Get-ChildItem $folder... failed but $folder | Get-ChildItem ... worked, when we do Get-ChildItem $folder, $folder is being passed as argument for the -Path parameter and the parameter type for it is [string[]]. So, in your code when $folder (a DirectoryInfo instance) is passed as argument, it is being converted to a string and, very unfortunately in Windows PowerShell, when we type convert a DirectoryInfo (and a FileInfo too!) object to string what we get as a result is the Directory Name (this is not the case in PowerShell Core, where the string representation of this object becomes the Directory FullName (a.k.a. Absolute Path) so Get-ChildItem thinks it's being fed a relative path and it looking for the folders in your current location.
However when we do $folder | Get-ChildItem ..., $folder gets bound to the -LiteralPath parameter by Property Name on the PSPath ETS property, in other words, the cmdlet receives the object's provider path (you can think of it as the absolute path of the folder) hence why it works fine.

Find the oldest file in each subdirectory with Powershell

My company recently moved to outlook365. We are entirely VDI based so our user profiles are stored on a single server. As a result our users all now have 2+ .ost files taking up storage space on the server. I'd like to write a script to find and delete the extraneous .ost files. In addition I'd like to schedule the script to run on a monthly basis to clean up any orphaned .ost's that occur for any other reason.
I've tried a few different solutions but can't seem to find the right syntax to identify just the oldest/original .ost in each subdirectory, all attempts have identified the oldest file from the whole directory or all .ost files in the directory.
$Path = "<path>"
$SubFolders = dir $Path -Recurse | Where-Object {$_.PSIsContainer} | ForEach-Object -Process {$_.FullName}
ForEach ($Folder in $SubFolders)
{
$FullFileName = dir $Folder | Where-Object {!$_.PSIsContainer} | Sort-Object {$_.LastWriteTime} -Descending | Select-Object -First 1
}
Inside of your loop, you could use the following to list the .ost file that has the oldest LastWriteTime value. Just add the -Descending flag to Sort-Object to list the newest file.
$FullFileName = foreach ($folder in $Subfolders) {
$Get-ChildItem -Path $folder -Recurse -File -Filter "*.ost" |
Sort-Object -Property LastWriteTime |
Select-Object -Property FullName -First 1
}
$FullFileName
If there is only one .ost file found in the $folder path, it will still find that file. So you will need logic to not delete when there is only one file. This does not guarantee it is the oldest file. You probably want a combination of the oldest CreationTime and newest LastWriteTime. The following will list the oldest .ost file based on CreationTime.
$FullFileName = foreach ($folder in $Subfolders) {
Get-ChildItem -Path $folder -Recurse -File -Filter "*.ost" |
Sort-Object -Property CreationTime |
Select-Object -Property FullName -First 1
}
$FullFileName
Another issue is setting the $FullFileName variable inside of the foreach loop. This means it will be overwritten through each loop iteration. Therefore, if you retrieve the value after the loop completes, it will only have the last value found. Setting the variable to be the result of the foreach loop output will create an array with multiple values.
To only output an OST file path when there are multiple OST files, you can do something like the following:
$FullFileName = foreach ($folder in $Subfolders) {
$files = Get-ChildItem -Path $folder -Recurse -File -Filter "*.ost" |
Sort-Object -Property LastWriteTime -Descending
if ($files.count -ge 2) {
$files | Select-Object -Property FullName -First 1
}
$FullFileName
This one liner should do the job, keeping the ost file with the newest LastWriteTime
gci -Path $Path -directory | where {(gci -Path $_\*.ost).count -gt 1}|%{gci -Path $_\*.cmd|Sort-Object LastWriteTime -Descending|Select-Object -Skip 1|Remove-Item -WhatIf}
Longer variant follows.
$Path = '<path>'
$Ext = '*.ost'
Get-ChildItem -Path $Path -directory -Recurse |
Where-Object {(Get-ChildItem -Path "$_\$Ext" -File -EA 0).Count -gt 1} |
ForEach-Object {
Get-ChildItem -Path "$_\$Ext" -File -EA 0| Sort-Object LastWriteTime -Descending |
Select-Object -Skip 1 | Remove-Item -WhatIf
}
The first two lines evaluate folders with more than one .ost file
The next lines iterates those folders and sort them descending by LastWriteTime, skips the first (newest) and pipes the other to Remove-Item with the -WhatIf parameter to only show what would be deleted while testing.
You can of course also move them to a backup location instead.

How to get the directory location for file recently modified

Suppose we have two directories C:\username\test1 & C:\username\test2. Both directories contain same file script.ps1. Now with powershell script I want to search the file script.ps1 in both directories & want the complete file location of file which is latest modified/created.
I was using below command but it did not give the desired output
Get-ChildItem -Path "C:\username" script.ps1 -Recurse | Where-object {!$_.psIsContainer -eq $true} | ForEach-Object -Process {$_.FullName} | select -last 1
For a given directory you can use
Get-ChildItem C:\dir1\dir2 -Recurse -ErrorAction SilentlyContinue | Where {!$_.PsIsContainer}|select Name,DirctoryName, LastWriteTime |Sort LastWriteTime -descending | select -first 1   Name DirctoryName LastWriteTime
And if you want it to run for multiple directories, you will have to run a loop on each directory:
Get-ChildItem C:\dir\* | Where {$_.PsIsContainer} | foreach-object { Get-ChildItem $_ -Recurse -ErrorAction Sile   ntlyContinue | Where {!$_.PsIsContainer} | Select Name,DirectoryName, LastWriteTime, Mode | Sort LastWriteTime -descend   ing | select -first 1}
It will list files which are last modified for each directories.
Edit: Search for a file
You can use following command to search for a file recursively if it is there in multiple directories:
Get-ChildItem -Path C:\Myfolder -Filter file.whatever -Recurse -ErrorAction SilentlyContinue -Force
This will list all versions of the file found, from newest to oldest:
Get-ChildItem -Path "C:\UserName" `
-File `
-Recurse `
-Include "Script.ps1" |
Sort-Object LastWriteTime -Descending |
Format-Table LastWriteTime, FullName -AutoSize
If you only want the most recent one, then replace the Format-Table line with:
Select-Object -First 1

find and recurse

I have a folder structure d:\domains\<domain>\folder1\folder2\folderx
There are maybe 20 <domain> folders, with differing levels of folders below them.
I want to search all folders for .php files, and just print the unique <domain> folders where they exit.
So for example, if there are files found in
d:\domains\domain1.com\test\test
d:\domains\domain2.com\test\test
d:\domains\domain2.com\test\help
I just want domain1.com,domain2.com to be printed. It needs to work in PowerShell v2.
I have the following, but it only prints the first domain?
Get-ChildItem -Path #(Get-ChildItem -Path D:\domains | Where-Object {$_.PSIsContainer})[1].FullName -Recurse *.php |
Select-Object -ExpandProperty FullName
Enumerate the domain folders, then filter for those of them that contain .php files.
Get-ChildItem 'D:\domains' | Where-Object {
$_.PSIsContainer -and
(Get-ChildItem $_.FullName -Filter '*.php' -Recurse)
}
If you have PowerShell v3 or newer you can use the -Directory switch instead of checking for $_.PSIsContainer:
Get-ChildItem 'D:\domains' -Directory | Where-Object {
Get-ChildItem $_.FullName -Filter '*.php' -Recurse
}
Select the Name property from the output if you want just the folder/domain names:
... | Select-Object -Expand Name

Get List of files Using Powershell - Modified between two dates

I'm using following command to get all the files which were modified before 20 hours and after 20 days past..
Get-ChildItem -Path '\\server\c$\Program Files (x86)\folder' -recurse -Filter *.* -include *.* |? {$_.LastWriteTime -lt (Get-Date).Addhours(-20) }|? {$_.LastWriteTime -gt (Get-Date).AddDays(-20)} | Select Fullname ,LASTWRITETIME | Sort-Object -Property LASTWRITETIME -Descending
It does gives me correct result.
But I'm getting Fullname as:
\\server\c$\Program Files (x86...
How can I get the fullname? Fullname is pretty long.. more than 260 character.
I've tried
Select -Expand Fullname
It works fine but i can't use it with LastWriteTime
Select -Expand Fullname, LastwriteTime
above command gives me error.
You can format output using Format-Table cmdlet like this:
$table_properties = #{Expression={$_.Fullname};Label="Full Name";width=195},
#{Expression={$_.LastWriteTime};Label="Last Write Time";width=35}
Get-ChildItem -Path '\\server\c$\Program Files (x86)\folder' -recurse -Filter *.* -include *.* |
? {$_.LastWriteTime -lt (Get-Date).Addhours(-20) }|
? {$_.LastWriteTime -gt (Get-Date).AddDays(-20)} |
Sort-Object -Property LASTWRITETIME -Descending |
Format-Table $table_properties
Instead of Select Fullname,LASTWRITETIME create custom object $table_properties with formatting parameters and pass it to Format-Table.
If your string is wider than PowerShell host display width than pipe output to Out-String -Width 500, where 500 is enough characters to display all fields.
See Creating custom tables article on TechNet.