Powershell - Find all extensions on network shares - powershell

I'm new to PS scripting (really, I started today) and, for a project, I need to create a .txt file with all the extensions from all shared folders on the local machine (a Windows file server).
I think I'm on the right path with this :
get-childitem -Path C:\test -Recurse | select extension -unique > $PSScriptRoot\ExtensionList.txt
It's doing exactly what I want for a given path and all subfolders but now I need to apply this to all shared folders on the machine.
I was able to list all the shared folder's path with this command :
$Shares= #(Get-WmiObject Win32_Share |
Select Name,Path,Type |
Where-Object { $_.Type -match '0|2147483648' } |
Select -ExpandProperty Path |
Select -Unique)
Write-Host $Shares
Now I'm stuck, I suppose I need to use the foreach command but I can't find the way to make it work.
Can someone help me put this together ?
Thanks,

You can try Get-SMBShare cmdLet:
Get-SMBShare | Foreach {
Get-ChildItem "\\$($_.name)" | Select-Object Extension -Unique
}

You're probably looking for something similar to this:
$Shares = #( Get-CimInstance Win32_Share | Where-Object { $_.Type -match '0|2147483648' } | Select -Unique )
ForEach ( $Share In $Shares ) { Get-ChildItem -Path $Share.Path -File -Recurse -ErrorAction Ignore | Select -Unique -ExpandProperty Extension }
I'll leave you to split the lines to match your particular style and to output to a file, (I'd advise that you consider using Out-File instead of > for that).

Thank you guys for your help! I was able to figure it out.
The following script will gather all extensions on shared folders, sort them, eliminate duplicates and empty lines, add "*' before the extension and create a file list.txt with the result.
#get shares
$Shares = #( Get-CimInstance Win32_Share |
Where-Object { $_.Type -match '0|2147483648' } |
Select -Unique )
#list all extensions
ForEach ( $Share In $Shares ) { Get-ChildItem -Path $Share.Path -File -Recurse -ErrorAction Ignore | Select -Unique -ExpandProperty Extension | out-file C:\extensions\List1.txt -append }
#remove empty lines
#(gc C:\extensions\List1.txt) -match '\S' | out-file C:\extensions\List2.txt
#Add * before extention type
gc C:\extensions\List2.txt | %{"*$_"} | out-file C:\extensions\List3.txt
#Sort by name
gc C:\extensions\List3.txt | sort | get-unique > C:\extensions\List4.txt
#Remove duplicates
$hash = #{}
gc C:\extensions\List4.txt |
%{if($hash.$_ -eq $null) { $_ }; $hash.$_ = 1} > C:\extensions\List.txt
#Delete list1-4
Remove-Item C:\extensions\List1.txt, C:\extensions\List2.txt, C:\extensions\List3.txt, C:\extensions\List4.txt

Related

Query File Version using powershell

I have a code that let me query one file version at the time.How can I query all file version inside of this registry key or if I just want to query specific files including firefox, chrome, etc.?
(Get-ItemProperty -Path 'HKLM:\Software\Microsoft\Windows\CurrentVersion\App Paths\communicator.exe').'(Default)' | ForEach-Object {
Get-ChildItem -Path $_ | Select-Object -ExpandProperty VersionInfo | Select FileDescription,ProductVersion
} | Format-Table -AutoSize
Instead of providing a literal path you should run Get-ChildItem on the "folder" the registry keys are contained in you are interested in. The result of this you pipe to your piece of code ... like this:
$Path = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\App Paths'
Get-ChildItem -Path $Path |
ForEach-Object {
Get-ItemProperty -Path $_.PSPath |
Select-Object -ExpandProperty '(default)' -ErrorAction SilentlyContinue |
ForEach-Object {
Get-Item -Path $_ -ErrorAction SilentlyContinue |
Select-Object FullName -ExpandProperty VersionInfo
}
} |
Format-Table -AutoSize

Write all running processes to a text file in PowerShell

The purpose of this code is to get a list of all used executables from a specific folder. After a month we will delete any exe's not on this list.
I currently get the correct results using this:
while ($true) {
foreach ($process in Get-Process | where {$_.Path -imatch 'ksv'} | select -Unique) {
$dir = $process | Get-ChildItem;
New-Object -TypeName PSObject -Property #{
'Path' = $process.Path;
} | Out-String | Add-Content -LiteralPath Z:\processList.txt
}
Get-Content Z:\processList.txt | sort | Get-Unique > Z:\uniqueprocesslist.txt
}
I'm going to get rid of the while loop as this will be eventually running as a service.
The problem with this is that it creates a huge list in processlist.txt that I would like to eliminate to save space.
I tried to come up with a better solution that scans the text file to see if the path is written already before adding the new process path. I am not sure what I am doing wrong but nothing is ever written to the text file
while ($true) {
foreach ($process in Get-Process | where {$_.Path -imatch 'ksv'} | select -Unique) {
$dir = $process | Get-ChildItem;
$progPath = New-Object -TypeName PSObject -Property #{
'Path' = $process.Path
}
$file = Get-Content "Z:\processList.txt"
$containsLine = $file | %{$_ -match $progPath}
if ($containsLine -contains $false) {
Add-Content -LiteralPath Z:\processList.txt
}
}
}
If I understand your question correctly you want to build a "recently used" list of executables in a specific directory in a file, and update that (unique) list with each run of your script.
Something like this should do that:
$listfile = 'Z:\processlist.txt'
# Build a dictionary from known paths, so that we can check for already known
# paths with an index lookup instead of a linear search over an array.
$list = #{}
if (Test-Path -LiteralPath $listfile) {
Get-Content $listfile | ForEach-Object {
$list[$_] = $true
}
}
# List processes, expand their path, then check if the path contains the
# string "ksv" and isn't already known. Append the results to the list file.
Get-Process |
Select-Object -Expand Path |
Sort-Object -Unique |
Where-Object {$_ -like '*ksv*' -and -not $list.ContainsKey($_)} |
Add-Content $listfile
Hashtable lookup and wildcard match are used for performance reasons, because they're significantly faster than linear searches in arrays and regular expression matches.
while ($true) {
$file = Get-Content "Z:\processList.txt"
$KSVPaths = Get-Process |
Where-Object {$_.Path -imatch 'ksv'} |
Select-Object -ExpandProperty Path |
Select-Object -Unique
ForEach ($KSVPath in $KSVPaths) {
if ($KSVPath -notin $file) {
Add-Content -Path $file -Value $KSVPath
}
}
}

Remove-Item cmdlet causes "Cannot find path" while process of removal of .exe files in local folder

I have script that selects .exe files with the specified name from the local folder and removes all files, except first.
$P variable is defined in param.
$P ="$($env:USERPROFILE)\Desktop\I"
Then I got this error
$C = Get-ChildItem $P -Filter *.exe| Where-Object Name -Like '*r_2-2*' | Sort-Object Name -Descending | Select-Object -ExpandProperty Name -Skip 1 | Remove-Item
Remove-Item : Cannot find path 'D:\FM\r_2-2.exe' because it does not exist.
At line:1 char:251
+ ... Descending | Select-Object -ExpandProperty Name -Skip 1 | Remove-Item
I know about foreach loop but want to use For-EachObject cmdlet instead.
You were quite close, if you want to use ForEach-Object:
Get-ChildItem $P -Filter *.exe | Where-Object Name -Like '*r_2-2*' | Select-Object -Skip 1 | ForEach-Object { remove-item $_.FullName -force }
To skip one first found result just Select-Object -Skip 1 is enough.
Remove-Item -Force also removes hidden and read-only files.
You can make the use of FullName parameter directly in your statement. Try this -
$C = Get-ChildItem $P -Filter *.exe| Where-Object Name -Like '*r_2-2*' | Sort-Object Name -Descending | Select-Object -ExpandProperty FullName -Skip 1
$c | ForEach-Object {Remove-Item -Path $_}
Use -Force parameter if you want to delete the hidden files too.

How ignore spaces and special characters in folder names

We have storage shared as \\192.168.1.26\e$. There are several folders (all shared) which are used by different departments. I wanted to get the owners and users who has access to folders, which are in those share folders.
I have created this PowerShell script for that:
$path = "\\192.168.1.26\e$\"
$getlist = Get-ChildItem -Path \\192.1681.26\e$ | Select-Object Name
foreach ($folder in $getlist) {
$out = '\\192.168.1.26\e$\{0}' -f $folder.name
#write-host $out
#prep arguments
$getargs = $out,'-ad | where {$_.psiscontainer -eq $true} | get-acl | Select-Object path,owner,accesstostring | fl'
write-host $getargs
gci $getargs
}
From $getlist = Get-ChildItem -Path \\192.1681.26\e$ | Select-Object Name I get all folders listed. However some folder names contains spaces and special characters, which causes the rest of the script to fail.
Examples:
It-&-Network
MIS
Account
Sales Reports
Market Report
DATA
When I run the below command it works as expected
gci \\192.168.1.26\e$\MIS | where {$_.psiscontainer -eq $true} | get-acl | Select-Object path,owner,group,accesstostring | fl
But I get an error on this command:
gci \\192.168.1.26\e$\Sales Reports | where {$_.psiscontainer -eq $true} | get-acl | Select-Object path,owner,group,accesstostring | fl
Is there any way to fix this?
Solution from Add double quotes to variable to escape space
$path = '\\localhost\f$\tmp'
$getlist = Get-ChildItem -Path $path | Select-Object Name
foreach ($folder in $getlist) {
$out = '"{0}\{1}"' -f $path,$folder.name
$getargs = $out
write-host $getargs
$extraparam = '-ad | where {$_.psiscontainer -eq $true} | get-acl | Select-Object path,owner,accesstostring | fl'
$run_command = 'gci {0} {1}' -f $getargs,$extraparam
write-host $run_command
Invoke-Expression $run_command
}
Also it can helps you - https://ss64.com/ps/syntax-esc.html
Updated for local disk example, working on my machine.

powershell truncates file fullnames ONLY as automated task

We have a file server that processes files that are received. When a file fails to process for whatever reason, it is moved into a failure folder. I've written a script to iterate through every possible one of these folders and spit out the FullName of the file into an e-mail which it sends to me.
Now when I run it manually, it works fine. However, when I set it as a scheduled task (running as Local System), the script still runs successfully, but the e-mail contains paths like \\blahblah\blah\blahblahblah\bl.....
I've tweaked the script a bunch of different ways and every time the output ends up the same. When I run it manually, it works as intended, when it runs as an automated script, it truncates the FullNames. I've found other people with this issue, but not as an automated task.
This is the relevant code of the script.
$emailFileList = ""
$filelist = #()
try {
GCI $topLevelPath -Recurse |
? { $_.PSIsContainer } |
ForEach-Object {
dir $_.FullName |
Where-Object {$_.FullName -like $unableToProcess} | ForEach-Object {
$filelist += dir $_.FullName
}
}
$emailFileList = Out-String -InputObject $($filelist | Select-Object FullName | Format-Table -AutoSize)
$emailBody = $emailBody + $emailFileList
}
EDIT:
I used the HTML method below but it added a bunch of junk markup. I added 4 lines to replace the markup with quotes. The inside of the try block now looks like this, and it works even as scheduled tasks.
GCI $topLevelPath -Recurse |
? { $_.PSIsContainer } |
ForEach-Object {
dir $_.FullName |
Where-Object {$_.FullName -like $unableToProcess} | ForEach-Object {
$filelist += dir $_.FullName
}
}
$emailFileList = $filelist | Select-Object FullName | ConvertTo-Html -fragment
$emailFileList = [regex]::Replace($emailFileList, "<table>.+</th></tr>", "")
$emailFileList = $emailFileList -replace '<tr><td>', '"'
$emailFileList = $emailFileList -replace '</td></tr>', """`r`n"
$emailFileList = $emailFileList -replace '</table>', ''
$emailBody = $emailBody + $emailFileList
I guess I also technically used regex on html what have I done noooooooo
Edit: Regardling answer "duplication" the problem above is SPECIFICALLY an interaction between powershell and the windows scheduled tasks.
This gives the kind of output you would probably expect
[command] | Format-Table -AutoSize | Out-String -Width 10000 #| clip.exe
Since you were using Format-Table -Autosize it was probably truncating due to the the amount of characters per line in the powershell instance. You can use the ConvertTo-Html function with the -Fragment command to create an HTML table.
Try something like this:
$emailFileList = ""
$filelist = #()
try {
Get-ChildItem $topLevelPath -Recurse `
| Where-Object -Property PSIsContainer -EQ -Value $True `
| ForEach-Object {
Get-ChildItem $_.FullName |
Where-Object -Property FullName -Like -Value $UnableToProcess `
| ForEach-Object {
$filelist += Get-ChildItem $_.FullName
}
}
$emailFileList = $filelist | Select-Object FullName | ConvertTo-Html -Fragment
$emailBody = $emailBody + $emailFileList
}
catch
{
}
Your problem is the formatter truncating because the console host can't render the full strings. Here's a solution where you'll get a txt with a list of names that can be used however you want
General rule of thumb: filter left, format right.
#Requires -Version 3
Try
{
Get-ChildItem -Path $TopLevelPath -Recurse -Folder |
## If you're on version 2, replace the -Folder switch with the following:
#Where-Object { $_.PSIsContainer }
ForEach-Object {
## If version 2, remove #().FullName and replace with
# | Select-Object -ExpandProperty FullName
$FileList = #(Get-ChildItem -Path $_.FullName -Filter "*$UnableToProcess*").FullName
}
$EmailBody += #($FileList)
}
Catch
{
}