Generating hashcodes for specific filetypes only with Powershell - powershell

I'm a complete beginner to Powershell and scripting, and have been successfully been using Out-GridView to display some properties of the files I have in my directories using the following:
dir D:\Folder1\$type -Recurse | Select Fullname,Directory,LastWriteTime | out-gridview
where I specifiy the file extension with $type = "*.pdf" for instance.
I would also like to start comparing files using hashcodes so I have tried this command:
ls | Get-Filehash
However, I would like to have the hashcodes in the output window as a seperate column with out-gridview. Is this possible? I've tried
dir D:\Folder1\$type -Recurse | Select Fullname,Directory,LastWriteTime,Filehash | out-gridview
and
dir D:\Folder1\$type -Recurse | Select Fullname,Directory,LastWriteTime | Get-Filehash | out-gridview
Of course neither of these work.
Does anyone have a way of generating hashcodes for a specific file extension only?
Many thanks in advance!

You can do this by using a calculated property with Select-Object:
Get-ChildItem -Path 'D:\Folder1\$type'-Recurse |
Select-Object FullName,Directory,LastWriteTime, #{Label='FileHash'; Expression={(Get-Filehash -Path $_.FullName).Hash}} |
Out-GridView
You should see a new column in the grid view called 'Filehash' that contains the SHA256 hash of the file. You can chage the algorithm (to, say, MD5) using the -Algorithm parameter of Get-FileHash.
If you're wondering what this is doing, the important parts are:
#{...}
signifies a hashtable. e.g. a set of key-value pairs
label
is the key that defines what your property (column name) will be in the grid view
expression defines the code snippet ({...}) that calculates the value of this property
$_
signifies that we are working with the 'current' object (file in this case) passing along the pipeline

Related

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.)

Import-CSV returns null when piped files

I have a piece of code that should grab all the .CSV files out of a directory and import them, using pipe character delimiters.
$apeasy = dir .\APEasy\*.csv | Import-CSV -delimiter '|'
The problem is this returns null. Without exception, no matter what I do.
The weird thing is that this works:
dir .\APEasy\*.csv
It returns a FileInfo object, which SHOULD be getting piped into Import-CSV as the file to import. In addition, these two commands work:
$csvFiles = dir .\Processed_Data_Review -Filter *.txt | Import-CSV -header(1..19) -delimiter "$([char]0x7C)"
dir .\LIMS -Filter *.csv | Import-CSV | ? {$_.SampleName -like "????-*"}| Export-CSV -Path .\lims_output.txt -NoTypeInformation
I really have no idea what's going on here. I'm dealing with a basic pipe-delimited file, quotations around every field (which is fine, I can import the data with those). Nothing special going on here. The file is THERE, Import-CSV just isn't GETTING it for some reason.
So my question is this: What could cause a file grabbed by 'dir' to fail to be piped into Import-CSV?
EDIT: The overall goal of this is to read the CSV files in a directory without knowing their name in advance, and output specific columns into a variety of output files.
EDIT: This is the line of code as it stands right now:
$apeasy = Get-ChildItem .\APEasy\*.csv | Select-Object -ExpandProperty FullName | Import-CSV -delimiter "$([char]0x7C)"
Isolating the Get-ChildItem statement, and isolating Get-Child and Select-Object both return what they should. A list of csv files in the directory, and an array of their full paths, respectively. Still, when they get piped into Import-CSV, they dissappear. Get-Member on the variable returns that it's empty.
Import-Csv accepts only strings (path) from the pipeline so in order to pipe directly to it you need to first expand the paths:
dir .\APEasy\*.csv |
select -expand fullname |
Import-CSV -delimiter '|'
Although cmdlets like Get-Content work that way in that they can accept the Path parameter by property name (and LiteralPath by value, which makes sense), Import-Csv is a little inconsistent. It only accepts the path to import by value:
-Path <String[]>
Specifies the path to the CSV file to import. You can also pipe a path to Import-Csv.
Required? false
Position? 1
Default value None
Accept pipeline input? true (ByValue)
Accept wildcard characters? false
So you could use
Get-ChildItem ... | Select-Object -ExpandProperty FullName | Import-Csv
but it won't work out of the pipeline directly.
This is a known bug in Import-Csv - even though you should be able to pipe Get-ChildItem (a.k.a dir) output directly to Import-Csv, that is still broken as of Windows PowerShell v5.1 / PowerShell Core v6.0.2.
Outputting the files' .PSPath property value is a way of working around the problem (.FullName works too) : The input objects' .PSPath property is what should be bound to Import-Csv's -LiteralPath parameter, but currently isn't.
Since you're collecting all input in memory anyway, you can simply use member-access enumeration (PSv3+) to access the .PSPath property on all matching files:
$apeasy = (Get-ChildItem .\APEasy\*.csv).PSPath | Import-CSV -delimiter '|'

Run a function on each element of a list in powershell

I have a directory full of file pairs. Each pair of files have the same name with the extensions of mp3 and cdg (karaoke files!). I would like to use powershell to get the list of all distinct file names with no extensions
I've gotten as far as:
dir -recurse -filter "*.mp3" | select-object Name | sort
But I can't quite figure out how to pass each Name to [System.IO.Path]::GetFileNameWithoutExtension
how would I do this?
What you're looking for is the for-each (%) filter (not precisely sure if it's a filter or a cmdlet but it has the same usage syntax).
Try the following
dir -recurse -filter "*.mp3" |
%{ $_.Name } |
%{ [IO::Path]::GetFileNameWithoutExtension($_) } |
sort
EDIT Update
I changed my answer from "select-object Name" to "%{ $_.Name}". The former essentially takes the Name property off of the pipeline value and creates a new object with a single property of the specified name with the value on the original object. The latter will process every value in the pipeline and pass the result of executing $_.Name down the pipeline.
dir -recurse -filter "*.mp3"| select #{name='Name';Expression={[System.IO.Path]::GetFileNameWithoutExtension($_.Name)}} | sort
If you hate typing %{$_.foo} all the time like I do, try Get-PropertyValue (alias: gpv) from PSCX.
More musings here for the suitably geeky: http://richardberg.net/blog/?p=55
Now that PowerShell v2 is RTMd, you can select the BaseName member:
dir -recurse -filter *.mp3 | select BaseName | sort

How to get Select-Object to return a raw type (e.g. String) rather than PSCustomObject?

The following code gives me an array of PSCustomObjects, how can I get it to return an array of Strings?
$files = Get-ChildItem $directory -Recurse | Select-Object FullName | Where-Object {!($_.psiscontainer)}
(As a secondary question, what's the psiscontainer part for? I copied that from an example online)
Post-Accept Edit: Two great answers, wish I could mark both of them. Have awarded the original answer.
You just need to pick out the property you want from the objects. FullName in this case.
$files = Get-ChildItem $directory -Recurse | Select-Object FullName | Where-Object {!($_.psiscontainer)} | foreach {$_.FullName}
Edit: Explanation for Mark, who asks, "What does the foreach do? What is that enumerating over?"
Sung Meister's explanation is very good, but I'll add a walkthrough here because it could be helpful.
The key concept is the pipeline. Picture a series of pingpong balls rolling down a narrow tube one after the other. These are the objects in the pipeline. Each stage of pipeline--the code segments separated by pipe (|) characters--has a pipe going into it and pipe going out of it. The output of one stage is connected to the input of the next stage. Each stage takes the objects as they arrive, does things to them, and sends them back out into the output pipeline or sends out new, replacement objects.
Get-ChildItem $directory -Recurse
Get-ChildItem walks through the filesystem creating FileSystemInfo objects that represent each file and directory it encounters, and puts them into the pipeline.
Select-Object FullName
Select-Object takes each FileSystemInfo object as it arrives, grabs the FullName property from it (which is a path in this case), puts that property into a brand new custom object it has created, and puts that custom object out into the pipeline.
Where-Object {!($_.psiscontainer)}
This is a filter. It takes each object, examines it, and sends it back out or discards it depending on some condition. Your code here has a bug, by the way. The custom objects that arrive here don't have a psiscontainer property. This stage doesn't actually do anything. Sung Meister's code is better.
foreach {$_.FullName}
Foreach, whose long name is ForEach-Object, grabs each object as it arrives, and here, grabs the FullName property, a string, from it. Now, here is the subtle part: Any value that isn't consumed, that is, isn't captured by a variable or suppressed in some way, is put into the output pipeline. As an experiment, try replacing that stage with this:
foreach {'hello'; $_.FullName; 1; 2; 3}
Actually try it out and examine the output. There are four values in that code block. None of them are consumed. Notice that they all appear in the output. Now try this:
foreach {'hello'; $_.FullName; $ x = 1; 2; 3}
Notice that one of the values is being captured by a variable. It doesn't appear in the output pipeline.
To get the string for the file name you can use
$files = Get-ChildItem $directory -Recurse | Where-Object {!($_.psiscontainer)} | Select-Object -ExpandProperty FullName
The -ExpandProperty parameter allows you to get back an object based on the type of the property specified.
Further testing shows that this did not work with V1, but that functionality is fixed as of the V2 CTP3.
For Question #1
I have removed "select-object" portion - it's redundant and moved "where" filter before "foreach" unlike dangph's answer - Filter as soon as possible so that you are dealing with only a subset of what you have to deal with in the next pipe line.
$files = Get-ChildItem $directory -Recurse | Where-Object {!$_.PsIsContainer} | foreach {$_.FullName}
That code snippet essentially reads
Get all files full path of all files recursively (Get-ChildItem $directory -Recurse)
Filter out directories (Where-Object {!$_.PsIsContainer})
Return full file name only (foreach {$_.FullName})
Save all file names into $files
Note that for foreach {$_.FullName}, in powershell, last statement in a script block ({...}) is returned, in this case $_.FullName of type string
If you really need to get a raw object, you don't need to do anything after getting rid of "select-object". If you were to use Select-Object but want to access raw object, use "PsBase", which is a totally different question(topic) - Refer to "What's up with PSBASE, PSEXTENDED, PSADAPTED, and PSOBJECT?" for more information on that subject
For Question #2
And also filtering by !$_.PsIsContainer means that you are excluding a container level objects - In your case, you are doing Get-ChildItem on a FileSystem provider(you can see PowerShell providers through Get-PsProvider), so the container is a DirectoryInfo(folder)
PsIsContainer means different things under different PowerShell providers;
e.g.) For Registry provider, PsIsContainer is of type Microsoft.Win32.RegistryKey
Try this:
>pushd HKLM:\SOFTWARE
>ls | gm
[UPDATE] to following question: What does the foreach do? What is that enumerating over?
To clarify, "foreach" is an alias for "Foreach-Object"
You can find out through,
get-help foreach
-- or --
get-alias foreach
Now in my answer, "foreach" is enumerating each object instance of type FileInfo returned from previous pipe (which has filtered directories). FileInfo has a property called FullName and that is what "foreach" is enumerating over.
And you reference object passed through pipeline through a special pipeline variable called "$_" which is of type FileInfo within the script block context of "foreach".
For V1, add the following filter to your profile:
filter Get-PropertyValue([string]$name) { $_.$name }
Then you can do this:
gci . -r | ?{!$_.psiscontainer} | Get-PropertyName fullname
BTW, if you are using the PowerShell Community Extensions you already have this.
Regarding the ability to use Select-Object -Expand in V2, it is a cute trick but not obvious and really isn't what Select-Object nor -Expand was meant for. -Expand is all about flattening like LINQ's SelectMany and Select-Object is about projection of multiple properties onto a custom object.