PowerShell Get-Childitem changes output - powershell

Noticed some weird behaviour with PowerShell Get-ChildItem. I do something like this in my script:
New-PSDrive -Name "R" -PSProvider "FileSystem"-Root "\\server\share"
$files = Get-ChildItem "R:/" | Select-Object -Property * -ExcludeProperty VersionInfo
$files | export-csv -Path "output.csv" -Delimiter ";"
The Get-ChildItem returns an object with properties such as BaseName, FullName, Parent, Root, ...
The problem I am having is that recently (or on some network shares which I mount) the properties of the object returned by Get-ChildItem changed. It used to be this list:
PSPath PSParentPath PSChildName PSDrive PSProvider PSIsContainer Mode BaseName Target LinkType Name FullName Parent Exists Root Extension CreationTime CreationTimeUtc LastAccessTime LastAccessTimeUtc LastWriteTime LastWriteTimeUtc Attributes
and as of recent (or as mentioned, for certain shared folder, haven't figured that out yet) the following properties were added:
Length DirectoryName Directory
In my CSV I now have 3 extra columns in between Name and IsReadOnly the aforementioned 3 properties are added. Does anyone know if this is due to an update of PowerShell or due to the specific server for which the shared folder was mounted?

As pointed out by #AdminOfThings, Get-ChildItem can return multiple object types: DirectoryInfo and FileInfo where only FileInfo has the properties Directory, DirectoryName and Length. Therefore, when exporting the results from Get-ChildItemto a CSV file. This file will only contain the 3 columns if the folder also contained files.
The following documentation describes the behaviour of export-csv in case you submit multiple object with different types Microsoft Powershell6.0:
When you submit multiple objects to Export-CSV, Export-CSV organizes the file based on the properties of the first object that you submit. If the remaining objects do not have one of the specified properties, the property value of that object is null, as represented by two consecutive commas. If the remaining objects have additional properties, those property values are not included in the file.
I will update the answer once I found a solution to make the CSV output consistent.

Related

Save directory tree to CSV, along with whether element is a file or folder in powershell

So far I've got this:
Get-ChildItem -Recurse 'C:\MyFolder' |
Select-Object FullName, name |
Export-Csv -path 'C:\output.csv' -noTypeInfo
It gives me a CSV with the full path name, and name of each folder & file in a directory.
However, I'd like a 3rd column that has either 'Folder' or 'File' (or something similar). Basically just a column that explicitly tells me whether something is a file or folder.
I feel like it should be a simple case of adding a new column like FullName, name, Type - but not sure what options are available.
Is this possible?
You could use the Attributes property however, this might give you more information than you really need, see FileAttributes Enum.
If you simply need a property with 2 values (File or Directory) you can use the boolean property PSIsContainer as reference to construct your new calculated property:
Get-ChildItem -Recurse 'C:\MyFolder' |
Select-Object FullName, Name, #{N='Type';E={('File', 'Directory')[$_.PSIsContainer]}} |
Export-Csv -path 'C:\output.csv' -NoTypeInformation

PowerShell, can't get LastWriteTime

I have this working, but need LastWriteTime and can't get it.
Get-ChildItem -Recurse | Select-String -Pattern "CYCLE" | Select-Object Path, Line, LastWriteTime
I get an empty column and zero Date-Time data
Select-String's output objects, which are of type Microsoft.PowerShell.Commands.MatchInfo, only contain the input file path (string), no other metadata such as LastWriteTime.
To obtain it, use a calculated property, combined with the common -PipelineVariable parameter,
which allows you to reference the input file at hand in the calculated property's expression script block as a System.IO.FileInfo instance as output by Get-ChildItem, whose .LastWriteTime property value you can return:
Get-ChildItem -File -Recurse -PipelineVariable file |
Select-String -Pattern "CYCLE" |
Select-Object Path,
Line,
#{
Name='LastWriteTime';
Expression={ $file.LastWriteTime }
}
Note how the pipeline variable, $file, must be passed without the leading $ (i.e. as file) as the -PipelineVariable argument . -PipelineVariable can be abbreviated to -pv.
LastWriteTime is a property of System.IO.FileSystemInfo, which is the base type of the items Get-ChildItem returns for the Filesystem provider (which is System.IO.FileInfo for files). Path and Line are properties of Microsoft.PowerShell.Commands.MatchInfo, which contains information about the match, not the file you passed in. Select-Object operates on the information piped into it, which comes from the previous expression in the pipeline, your Select-String in this case.
You can't do this as a (well-written) one-liner if you want the file name, line match, and the last write time of the actual file to be returned. I recommend using an intermediary PSCustomObject for this and we can loop over the found files and matches individually:
# Use -File to only get file objects
$foundMatchesInFiles = Get-ChildItem -Recurse -File | ForEach-Object {
# Assign $PSItem/$_ to $file since we will need it in the second loop
$file = $_
# Run Select-String on each found file
$file | Select-String -Pattern CYCLE | ForEach-Object {
[PSCustomObject]#{
Path = $_.Path
Line = $_.Line
FileLastWriteTime = $file.LastWriteTime
}
}
}
Note: I used a slightly altered name of FileLastWriteTime to exemplify that this comes from the returned file and not the match provided by Select-String, but you could use LastWriteTime if you wish to retain the original property name.
Now $foundMatchesInFiles will be a collection of files which have CYCLE occurring within them, the path of the file itself (as returned by Select-String), and the last write time of the file itself as was returned by the initial Get-ChildItem.
Additional considerations
You could also use Select-Object and computed properties but IMO the above is a more concise approach when merging properties from unrelated objects together. While not a poor approach, Select-Object outputs data with a type containing the original object type name (e.g. Selected.Microsoft.PowerShell.Commands.MatchInfo). The code may work fine but can cause some confusion when others who may consume this object in the future inspect the output members. LastWriteTime, for example, belongs to FileSystemInfo, not MatchInfo. Another developer may not understand where the property came from at first if it has the MatchInfo type referenced. It is generally a better design to create a new object with the merged properties.
That said this is a minor issue which largely comes down to stylistic preference and whether this object might be consumed by others aside from you. I write modules and scripts that many other teams in my organization consume so this is a concern for me. It may not be for you. #mklement0's answer is an excellent example of how to use computed properties with Select-Object to achieve the same functional result as this answer.

How to use Get-Content to get all information from the most recent file

I am trying to use Get-Content to get the most recent .xml file and all its content to be displayed in the powershell window, but I am having a hard time.
I have use the the following:
Get-ChildItem "\\Server1\c$\Program Files\AAA\Logs\" | Sort-Object CreationTime | Select-Object -Last 1
Get-Content -Path "\\Server1\c$\Program Files\AAA\Logs\" | Where-Object {$_.LastWriteTime -lt (get-date).addDays(-1)} | Select -Last 1
But I cannot figure out how to go about grabbing the latest file and displaying all its content in the console
You are close. You have to pipe the result of your first line to Get-Content:
Get-ChildItem "\\Server1\c$\Program Files\AAA\Logs\" | Sort-Object CreationTime | Select-Object -Last 1 | Get-Content
Your second line does not make much sense. If you provide a valid path to Get-Content, it will return to you the content of the file as a string. You cannot apply any creation time logic to this content afterwards with Where-Object.
Your first line though, works like this:
It gets all files and folders that are contained in your given path. If this path really just contains valid log files, you can leave it like this. Otherwise you should filter this result, so you really just get your desired files. To be precise, Get-ChildItem returns an array of System.IO.FileInfo objects. They contain a lot of information about your files.
You then sort this array of System.IO.FileInfo objects by the CreationTime property with Sort-Object.
Finally, you select the last element of the sorted array. This is still a System.IO.FileInfo object. That's why you see some of its properties in your output.
If you then pipe this System.IO.FileInfo object to Get-Content, the FullPath property of this object will be mapped to the -Path parameter of Get-Content, thus returning the content of the file specified by the System.IO.FileInfo object.

Use Powershell to list the Fully Pathed Filenames on Individual Separate Lines?

If I execute:
Get-ChildItem *.ext -recurse
the output consists of a series of Directory sections followed by one or more columns of info for each matching file separated by said directory sections. Is there something like the Unix find command? In which each matching file name appears on a single line with its full relative path?
Get-Childitem by default outputs a view for format-table defined in a format xml file somewhere.
get-childitem | format-table
get-childitem | format-list *
shows you the actual properties in the objects being output. See also How to list all properties of a PowerShell object . Then you can pick and choose the ones you want. This would give the full pathname:
get-childitem | select fullname
If you want an output to be just a string and not an object:
get-childitem | select -expand fullname
get-childitem | foreach fullname
Resolve-Path with the -Relative switch can be used to display the relative paths of a set of paths. You can collect the full path names (FullName property) from the Get-ChildItem command and use the member access operator . to grab the path values only.
Resolve-Path -Path (Get-ChildItem -Filter *.ext -Recurse).FullName -Relative
Note: The relative paths here only accurately reflect files found within the current directory (Get-ChildItem -Path .), i.e. Get-ChildItem -Path NotCurrentDirectory could have undesirable results.
Get-ChildItem's -Name switch does what you want:
It outputs the relative paths (possibly including subdir. components) of matching files as strings (type [string]).
# Lists file / dir. paths as *relative paths* (strings).
# (relative to the input dir, which is implicitly the current one here).
Get-ChildItem -Filter *.ext -Recurse -Name
Note that I've used -Filter, which significantly speeds up the traversal.
Caveat: As of PowerShell 7.0, -Name suffers from performance problems and behavioral quirks; see these GitHub issues:
https://github.com/PowerShell/PowerShell/issues/9014
https://github.com/PowerShell/PowerShell/issues/9119
https://github.com/PowerShell/PowerShell/issues/9126
https://github.com/PowerShell/PowerShell/issues/9122
https://github.com/PowerShell/PowerShell/issues/9120
I am having some problem passing the path plus filename to a parser. There are about 90 files of 1 GB each involved in my task. Each of the file is contained in a folder of its own. All of the folders are contained under a parent folder.
Goal: Ideally, I would like to parse 20 files simultaneously for multitasking and continue to the next 20 until all 90 files are done.
This would mean that I would like to spawn some concurrent parsing of 20 files in a batch at any one given time. In carrying out the parsing, I would like to use measure-command to time the work from beginning to finish.
Script I have used:
Get-ChildItem –Path "E:\\OoonaFTP\\input\\Videos3\\" -Filter *.mp4 -recurse | select -expand fullname
Foreach-Object {
Measure-Command { "E:\OoonaFTP\Ooona_x64_ver_2.5.13\OoonaParser.exe -encode -dat -drm $_.FullName" } | Select-Object -Property TotalSeconds
}
===============================
I have this working batch script with a for statement but doing each iteration one after another. This is not what is the ideal case though. I would really like to accomplish this in PowerShell and with simultaneous tasks.
Could someone please suggest some ways by which I could accomplish this?
Thank you very much!
Thanks for the various suggestions. I'm curious that some of them lead to empty output in my Powershell (PSVersion: 5.1.18362.145).
I tried a number of these and, inspired by some of them, found the best answer for my case at the moment:
Get-ChildItem *.ext -recurse | Select-Object -property fullname
(When I made the window wide enough I got all the info I needed; in general I suppose I might need to do more to get the formatting I want.)

Powershell - using dir / GetChildItem to list directories with Date Modified

So currently I am able to get a list of my directories with the folder name using
dir -directory -name
And I also know I can use recurse as well to list sub-directories
Thank you to Sany
What I would like to create in this list is to show this folder's date modified value. I've looked over the documentation and I'm having difficulty to find the answer I assume if it is there it is likely part of Attributes but I'm uncertain as to how to format it correctly.
Most of the searches I've done are have turned up about excluding files based on date modified, rather than showing the date instead.
You can use Select-Object and what I like to use Export-Csv
Get-ChildItem C:/temp -directory -recurse | Select-Object FullName, LastWriteTime | Export-Csv -Path list_my_folders.csv -NoTypeInformation
In case you want extract other information as well you can also remove the Select-Object part and you will see all columns which you can select.
Output:
"FullName","LastWriteTime"
"C:\temp\save","21.11.2019 15:34:27"
"C:\temp\test","12.01.2020 05:13:24"
"C:\temp\test\002custom","14.12.2019 01:17:54"
"C:\temp\test\002normal","14.12.2019 01:31:46"
"C:\temp\test\x","13.01.2020 12:51:05"
"C:\temp\test\002normal\normal","14.12.2019 01:31:53"
"C:\temp\test\x\Neuer Ordner","13.01.2020 12:51:05"
Of course you can also use it without Export-Csv:
Get-ChildItem C:/temp -directory -recurse | Select-Object FullName, LastWriteTime > list_my_folders.txt
But the output is in a format that is harder to work in most cases:
FullName LastWriteTime
-------- -------------
C:\temp\save 21.11.2019 15:34:27
C:\temp\test 12.01.2020 05:13:24
C:\temp\test\002custom 14.12.2019 01:17:54
C:\temp\test\002normal 14.12.2019 01:31:46
C:\temp\test\x 13.01.2020 12:51:05
C:\temp\test\002normal\normal 14.12.2019 01:31:53
C:\temp\test\x\Neuer Ordner 13.01.2020 12:51:05