Is it possible to use comments in Powershell multiple line commands? - powershell

Debugging and testing multiline commands in Powershell ISE has been bugging me for years. I like having multiple line commands because they are easy to read, but they make things harder to debug. As an example, I'm using the following command to get folders older than $days (which by the way works).
$dirs = Get-ChildItem $targetDir -Directory -exclude *.ps1 `
| Where CreationTime -gt (Get-Date).AddDays(-1 * $days) `
| Sort-Object -Property LastWriteTime
I'd like to change AddDays to AddMinutes to test different result sets but I want to leave the original line in so I can easily switch back and forth. Below I copied the line I want to keep and commented it out, and on the new line changed AddDays to AddMinutes Adding a # breaks the multiline feature. Is there an easy way around this I don't have to cut my copied line and move it "out" of the command? Or is there a way to split/unsplit a command into and out of multiple lines?
$dirs = Get-ChildItem $targetDir -Directory -exclude *.ps1 `
# | Where CreationTime -gt (Get-Date).AddDays(-1 * $days) `
| Where CreationTime -gt (Get-Date).AddMinutes(-1 * $days) `
| Sort-Object -Property LastWriteTime
(above does not work due to commented out line)

Use the multiline comment syntax instead of #.
<# comment #>
This should allow you to comment text within a multi-line command.
However, this works only if you are using Powershell 2.0

As powershell expects a continuation after a | or a ,
as the last char in a line you don't need the backtick and
you could format differently, then the single line comment in a longer pipe still works:
$dirs = Get-ChildItem $targetDir -Directory -exclude *.ps1 |
# Where CreationTime -gt (Get-Date).AddDays(-1 * $days) |
Where CreationTime -gt (Get-Date).AddMinutes(-1 * $minutes) |
Sort-Object -Property LastWriteTime

your problem is the [icky, nasty] backticks. [grin] powershell knows there is more coming after a pipe ... so there is no need to add a backtick if you put the pipe at the end of the segment that is being piped. like this ...
$dirs = Get-ChildItem $targetDir -Directory -exclude *.ps1 |
# Where CreationTime -gt (Get-Date).AddDays(-1 * $days) |
Where CreationTime -gt (Get-Date).AddMinutes(-1 * $days) |
Sort-Object -Property LastWriteTime

Try this, which can be included as a multiline comment example
$dirs = Get-ChildItem $targetDir -Directory -exclude *.ps1 `
<# | Where CreationTime -gt (Get-Date).AddDays(-1 * $days) #> ` | Where CreationTime -gt (Get-Date).AddMinutes(-1 * $days) `
| Sort-Object -Property LastWriteTime

Related

Select multiple files by file name and get content

I run this script for my monitoring system, but i want to extend the range of the aviable date.
Is there any way to get the content of multiple files with different names. Currently im only looking for one specific name pattern, for example, for all files which include a specific date like 2022-08-08*.log.
So what i want to do, is to collect all files from 7 days ago up to 1 day ago at the same time and get the content.
$backuppath = "random-name-in-logfile"
$yest = (get-date (get-date).addDays(-1) -UFormat "%Y-%m-%d")
# check for pattern in files
$path1 = Get-ChildItem `
-Path "C:\path\to\log" -Filter "$yest*.log" -recurse | `
Select-String -pattern ([regex]::escape($backuppath)) | `
Select-Object -Property Path
# transform string to usable path
$path2 = $path1 -replace ('#{Path=','') -replace ('}','')
# check for more details
$analyze = Get-Content $path2 | Select-String -pattern "Pattern" -SimpleMatch
Ok i think i got it.
It´s easier to do by setting the creation time to a limit like 1 day (or for my example 7 days)
$path1 = Get-ChildItem `
-Path "C:\path\to\log" -Filter "*.log" -recurse | `
Where-Object { $_.CreationTime -gt (Get-Date).AddDays(-7) } | `
Select-String -pattern ([regex]::escape($backuppath)) | `
Select-Object -Property Path

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

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.

Powershell, trying to output only the path and lastwritetime on directories

I am trying to write a script that will output any directory that has not changed in over 90 days. I want the script to only show the entire path name and lastwritetime. The script that I wrote only shows the path name but not the lastwritetime. Below is the script.
Get-ChildItem | Where {$_.mode -match "d"} | Get-Acl |
Format-Table #{Label="Path";Expression={Convert-Path $_.Path}},lastwritetime
When I run this script, I get the following output:
Path lastwritetime
---- ----------
C:\69a0b021087f270e1f5c
C:\7ae3c67c5753d5a4599b1a
C:\cf
C:\compaq
C:\CPQSYSTEM
C:\Documents and Settings
C:\downloads
I discovered that the get-acl command does not have lastwritetime as a member. So how can I get the needed output for only the path and lastwritetime?
You don't need to use Get-Acl and for perf use $_.PSIsContainer instead of using a regex match on the Mode property. Try this instead:
Get-ChildItem -Recurse -Force |
? {$_.PSIsContainer -and ($_.LastWriteTime -lt (get-date).AddDays(-90))} |
Format-Table FullName,LastWriteTime -auto
You may also want to use -Force to list hidden/system dirs. To output this data to a file, you have several options:
Get-ChildItem -Recurse -Force |
? {$_.PSIsContainer -and ($_.LastWriteTime -lt (get-date).AddDays(-90))} |
Select LastWriteTime,FullName | Export-Csv foo.txt
If you are not interested in CSV format try this:
Get-ChildItem -Recurse -Force |
? {$_.PSIsContainer -and ($_.LastWriteTime -lt (get-date).AddDays(-90))} |
Foreach { "{0,23} {1}" -f $_.LastWriteTime,$_.FullName} > foo.txt
Also try using Get-Member to see what properties are on files & dirs e.g.:
Get-ChildItem $Home | Get-Member
And to see all values do this:
Get-ChildItem $Home | Format-List * -force