Put the drive letter in the path - powershell

$DiskCount = (Get-Disk | Where-Object {$_.BusType -eq "USB"}).Number.Count
if ($DiskCount -eq 1) {
filter Get-FirstResolvedPath {
(Get-Disk |
Where-Object {$_.BusType -eq "USB"} |
Get-Partition |
Get-Volume |
Where-Object {$null -ne $_.DriveLetter}
).DriveLetter + ':\' | Join-Path -ChildPath $_ -Resolve -ErrorAction SilentlyContinue
}
'Folder\Folder\reg\Start.reg' | Get-FirstResolvedPath
}
Is there another method to get full path to the file, if it stores on USB drive, the path is absolute, and we don't know USB disk letter?

I like your solution. I would write it differently, and you could shorten it a bit:
function Get-ResolvedPath {
param ([Parameter(ValueFromPipeline=1)]$Path)
Get-Disk |? BusType -ne USB | Get-Partition |% {Join-Path ($_.DriveLetter+":") $Path -R -EA Silent}
}
'Folder\Folder\reg\Start.reg' | Get-ResolvedPath | select -First 1

Related

Exporting Results of Script to CSV

I've been searching for a script that simply lists folders in a share and their size. I found the following but I'm getting hung up on how to export the output I'm seeing to an easy to read CSV. It amazes me how something so simple has turned into something difficult. Suggestions welcome!
$colItems = Get-ChildItem "C:\Users\user.name\Desktop" | Where-Object {$_.PSIsContainer -eq $true} | Sort-Object
foreach ($i in $colItems)
{
$subFolderItems = Get-ChildItem $i.FullName -recurse -force | Where-Object {$_.PSIsContainer -eq $false} | Measure-Object -property Length -sum | Select-Object Sum
$i.FullName + " -- " + "{0:N2}" -f ($subFolderItems.sum / 1MB) + " MB"
}
Based on your script:
## C:\Users\UserName\Desktop\Test\SO_50359947.ps1
$colItems = Get-ChildItem "$($Env:USERPROFILE)\Desktop" |
Where-Object {$_.PSIsContainer} | Sort-Object
$data = ForEach ($i in $colItems){
$subFolderItems = Get-ChildItem $i.FullName -recurse -force -ea 0|
Where-Object {!$_.PSIsContainer} |
Measure-Object -Property Length -sum | Select-Object Sum
[PSCustomObject]#{
Folder = $i.FullName
Size = "{0,10:N2} MB" -f ($subFolderItems.sum / 1MB)
}
}
$data
#$data | Export-Csv "$($Env:USERPROFILE)\Desktop\your.csv" -NoType
Sample output (on a different tree)
> $data
Folder Size
------ ----
Q:\test\2018\03 0,37 MB
Q:\test\2018\04 0,83 MB
Q:\test\2018\05 383,57 MB
Uncomment the last line to write to a csv file.
Here is one way using custom objects:
Get-ChildItem "$home\Desktop" -Directory |
ForEach-Object {
$contents = Get-ChildItem -Path $_.FullName -Recurse
[PsCustomObject]#{
FolderName = $_.Name
SizeMB = [Math]::Round(($contents | Where-Object PsIsContainer -eq $false | Measure-Object -property Length -Sum).Sum / 1MB,2)
SubFolders = ($contents | Where-Object PsIsContainer -eq $true | Measure-Object).Count
Files = ($contents | Where-Object PsIsContainer -eq $false | Measure-Object).Count
}
}
This gives output like this:
FolderName SizeMB SubFolders Files
---------- ------ ---------- -----
Folder1 438.38 19 124
Folder2 34925.72 306 3779
To send this to CSV, simply append the following after the last bracket:
| Export-Csv "$home\Desktop\Folders.csv" -NoTypeInformation
There's a couple ways to do this, but I think the least confusing would be to simply add that information to the item using Add-Member. Then output the desired data via Export-Csv.
$colItems = Get-ChildItem "C:\Users\user.name\Desktop" |
Where-Object {$_.PSIsContainer -eq $true} |
Sort-Object |
%{ Add-Member -InputObject $_ -NotePropertyName 'FolderSize' -NotePropertyValue (Get-ChildItem $_.FullName -recurse -force | Where-Object {$_.PSIsContainer -eq $false} | Measure-Object -property Length -sum | Select-Object -ExpandProperty Sum) -PassThru}
$colItems | Select FullName,FolderSize | Export-Csv -NoType
Please assign to some variable after getting all the file details from the desktop/any location and create the excel
$subFolderItems = Get-ChildItem "$home\Desktop" -Directory |
ForEach-Object {
$contents = Get-ChildItem -Path $_.FullName -Recurse
[PsCustomObject]#{
FolderName = $_.Name
SizeMB = [Math]::Round(($contents | Where-Object PsIsContainer -eq $false | Measure-Object -property Length -Sum).Sum / 1MB,2)
SubFolders = ($contents | Where-Object PsIsContainer -eq $true | Measure-Object).Count
Files = ($contents | Where-Object PsIsContainer -eq $false | Measure-Object).Count
}
}
$subFolderItems | out-file C:\Users\thiyagu.a.selvaraj\Desktop\PowerShell\FileSizeOutput.xls
In my opinion, using CSV for your use case isn't the best plan because (in my folder) there are files that are over 1000 MB, so csv will break up some file sizes. To make a tab delimited file, which can just as easily be parsed by most systems, simply modify your scripts output string.
I changed the -- to a tab character, and added an output at the end of the line
"{0}`t{1:N2} MB" -f $i.fullname,($subFolderItems.sum / 1MB) >> output.csv
The final script is below; you will probably need to change output.csv to your prefered output file location.
$colItems = Get-ChildItem "C:\Users\user.name\Desktop" | Where-Object {$_.PSIsContainer -eq $true} | Sort-Object
foreach ($i in $colItems)
{
$subFolderItems = Get-ChildItem $i.FullName -recurse -force | Where-Object {$_.PSIsContainer -eq $false} | Measure-Object -property Length -sum | Select-Object Sum
"{0}`t{1:N2} MB" -f $i.fullname,($subFolderItems.sum / 1MB) >> output.csv
}
If you don't insist on using pure powershell, on Windows 10 (sufficiently new) you can just call bash:
bash -c "du -h | sed 's/\s\+/,/'"
To show how it affects the resulting csv, a longer example:
bash -c "du -m | sed 's/\([0-9]\+\)\s\+\(.*\)/\1MB,\2/'"

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.

Out-File only if Results are available. No Zero K files

Is there a way when you use Get-ChildItem with a Where-Object clause to have it produce the results in a text file only if there are results?
Get-ChildItem -path \\$server\e$ -Recurse | Where-Object {$_.name -eq help.txt} | `
out-file "c:\temp\$server.txt"
The above will produce a file regardless if there are results. I'm having trouble telling implementing the logic to only create when results are available.
You can't do it that way. You'll have to do it in 2 parts:
$results = Get-ChildItem -path \\$server\e$ -Recurse | Where-Object {$_.name -eq help.txt}
if ($results) {
$results | out-file "c:\temp\$server.txt"
}
Seems to work how you want if you use Set-Content instead of Out-File.
Get-ChildItem -path \\$server\e$ -Recurse | Where-Object {$_.name -eq help.txt} |
Set-Content "c:\temp\$server.txt"
#or
gci -R \\$server\e$ |? Name -eq "help.txt" | sc "c:\temp\$server.txt"

Converting list of strings to numbers

I have a directory whose subdirectories are all numbers:
./2856
./2357
./10198
and so on.
I'm trying to write a Powershell script that would return the largest subdirectory name smaller than X.
So in this example, for the input 3000 it should return 2856.
However what I've written so far looks very cumbersome to me, and I'm wondering how it can be shortened:
Get-ChildItem "$path" `
| ?{ $_.PSIsContainer } `
| Select-Object #{Name="AsInt"; Expression={[int]$_.Name}} `
| Select-Object -expand AsInt `
| ?{$_ -lt [int]$lessThanNumber} `
| measure-object -max `
| Select-Object -expand Maximum
I tried this with PowerShell v3:
$max = 3000
$cur = 0
ls -d | %{
# Potential for issues if the directory name cannot be cast to [int]
$name = ([int]$_.Name)
if (($name -gt $cur) -and ($name -le $max)) {
$cur = $name
}
}
($cur = 2856 at the end)
With PowerShell v3:
Get-ChildItem $path -dir | Select *,#{n='Number';e={[int]$_.Name}} |
Where Number -lt $lessThanNumber | Sort Number | Select -Last 1
If you have V3:
#(Get-ChildItem -Path $Path -Directory -Name |
ForEach-Object {$_ -as [int]}) -lt $LessThanNumber |
sort | select -last 1
You can try :
Get-ChildItem "$path" | Where-Object {$_.PSIsContainer -and [int]$_.name -le 3000} `
| Sort-Object -Property #{exp={[int]$_.name}} `
| Select-Object -Last 1
You can write it :
Get-ChildItem "$path" | ? {$_.PSIsContainer -and [int]$_.name -le 3000} `
| Sort -Property #{exp={[int]$_.name}} `
| Select -Last 1
If you want to avoid errors due to these directory names which are not integers :
Get-ChildItem "$path" | ? {$_.PSIsContainer -and ($_.name -as [int]) -le 3000} `
| Sort -Property #{exp={$_.name -as [int]}} `
| Select -Last 1
Yet another (v3) example. Only directory names that contain numbers are passed on and the Invoke-Expression cmdlet is used to evaluate the name into a number (no explicit cast is needed)
$x = 3000
Get-ChildItem -Directory | Where-Object {
$_.Name -notmatch '\D' -and (Invoke-Expression $_.Name) -lt $x
} | Sort-Object | Select-Object -Last 1

Using an array of wildcards in a Powershell WHERE clause

I'm trying to make the following PowerShell script more generic. I want to pass in an array of excludes rather than a fixed list. I can't figure out how to do this except my partial solution below:
ORIGINAL
This gets all the files in a path except a list of wildcard files or folders:
Get-ChildItem -Path "$sitePath" -Recurse | `
where {!$_.PSIsContainer } | `
Select -ExpandProperty FullName | `
Where {$_ -notlike "$sitePath\Custom\*"} | `
Where {$_ -notlike "$sitePath\Download\*"} | `
Where {$_ -notlike "$sitePath\Temp\*"} | `
Where {$_ -notlike "$sitePath\Portal\*"} | `
Where {$_ -notlike "$sitePath\web.config*"} | `
SELECT $_
PARTIAL SOLUTION
This is the best I've come up with. It allows me to create an array of wildcards called $excludeList, but is limited and is slightly slower:
$excludeList = #("$sitePath\Custom\*",
"$sitePath\Download\*",
"$sitePath\Portal\*",
"$sitePath\web.config*")
Get-ChildItem -Path "$sitePath" -Recurse | `
where {!$_.PSIsContainer } | `
Select -ExpandProperty FullName | `
Where {$_ -notlike $excludeList[0]} | `
Where {$_ -notlike $excludeList[1]} | `
Where {$_ -notlike $excludeList[2]} | `
Where {$_ -notlike $excludeList[3]} | `
Where {$_ -notlike $excludeList[4]} | `
Where {$_ -notlike $excludeList[5]} | `
Where {$_ -notlike $excludeList[6]} | `
Where {$_ -notlike $excludeList[7]} | `
Where {$_ -notlike $excludeList[8]} | `
Where {$_ -notlike $excludeList[9]} | `
Where {$_ -notlike $excludeList[10]} | `
SELECT $_
Is there a better way to pass an array in to the where clause? All the solutions I've found only allow non-wildcard matches.
Hope someone can help!
One approach would be to iterate over the items in the exclude list, and only include a path if it does not match any of the exclusions:
$excludeList = #("$sitePath\Custom\*",
"$sitePath\Download\*",
"$sitePath\Portal\*",
"$sitePath\web.config*")
Get-ChildItem -Path "$sitePath" -Recurse |
where { !$_.PSIsContainer } |
select -ExpandProperty FullName |
where { $path = $_; -not #($excludeList | ? { $path -like $_ }) }
If all of your excluded items follow the same pattern, you can also simplify the exclude list by moving the common pattern to the like call:
$excludeList = #('Custom','Download','Portal','web.config')
Get-ChildItem -Path "$sitePath" -Recurse |
where { !$_.PSIsContainer } |
select -ExpandProperty FullName |
where { $path = $_; -not #($excludeList | ? { $path -like "$sitePath\$_*" }) }
If you're willing to go with a regex instead, you can simplify this a lot:
$excludeList = [regex]::Escape("$sitePath\Custom\"),
[regex]::Escape("$sitePath\Download\"),
[regex]::Escape("$sitePath\Temp\") -join "|"
Get-ChildItem $sitePath -Recurse | `
where {!$_.PSIsContainer } | `
Select -ExpandProperty FullName | `
Where {$_ -notmatch $excludeList}
Not sure why you have the trailing Select $_, it is unnecessary AFAICT.
You should try -contains and -notcontains operators if you are trying to compare against an array.
$array = "name1","name2","name3"
#This will return false
$array -contains "name"
#this will return true
$array -notcontains "name"