I am working on a PowerShell command to search across drives for a specific file. I am new to PowerShell so most of what I have already is just stuff I found online. At the moment I have this:
$ExclDrives = ('C')
>> Get-PSDrive -PSProvider FileSystem | Where-Object {$_.Name -notin $ExclDrives} `
>> | % {write-host -f Green "Searching " $_.Root;get-childitem $_.Root -include *MyFile.txt -r `
>> | sort-object Length -descending}
Which outputs this:
Searching D:\
Searching E:\
Searching F:\
Directory: F:\MyDirectory
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 8/13/2022 12:03 AM 0 MyFile.txt
PS C:\Windows\system32>
I would like to know how I can take the directory that is listed in the output and use it in a following command such as:
cd F:\MyDirectory
If this is possible through piping or something I would really appreciate an answer :)
Thanks for reading
I wasn't really sure what the best way to handle this would be if multiple files were found. We wouldn't be able to change directory into the parent folders while the script was running nor would we be able to do so for all of the returned files unless we opened new PowerShell windows for each. Since it appears that you will be searching for specific files which I assume will not return too many results and not knowing your ultimate goal I went with opening a new file explorer window for each file with the file being highlighted/selected.
$excludeDrives = ('C')
Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Name -notin $excludeDrives } |
ForEach-Object {
Write-Host -f Green 'Searching ' $_.Root
Get-ChildItem -Path $_.Root -Recurse -Include *MyFile.txt -ErrorAction SilentlyContinue |
ForEach-Object {
# This line will open a file explorer window with the file highlighted
explorer.exe /select, $_
# This line will send the file object out through the pipeline
$_
} | Sort-Object Length -Descending
}
To answer your question about how to access the file's directory in the next command, you can use Foreach-Object and $_.Directory:
Get-ChildItem -Path $_.Root -Recurse -Include *MyFile.txt -ErrorAction SilentlyContinue |
Sort-Object Length -Descending |
ForEach-Object {
# Using the pipeline we can pass object along and access them
# using a special automatic variable called $_
# a property exists on FileInfo objects called Directory
'The directory is ' + $_.Directory
}
UPDATE
Hopefully this will answer the question in your comment
$ExclDrives = ('C')
Get-PSDrive -PSProvider FileSystem |
Where-Object { $_.Name -in $ExclDrives } |
ForEach-Object {
Write-Host -f Green 'Searching ' $_.Root
Get-ChildItem $_.Root -Include *MyFile.txt -Recurse -ErrorAction SilentlyContinue |
ForEach-Object {
# do whatever you want with the file. Reference using $_
Write-Host "Found Filename: $($_.Name)`tDirectory: $($_.Directory)" -ForegroundColor Cyan
explorer.exe /select, $_
# output the fileinfo object, in this case
# to the next command in the pipeline which is Sort-Object
$_
} |
Sort-Object Length -Descending
}
Related
I´m still quite new to PS, so it might be easy to answer for the veterans.
I want to grab the path of a file after a search through the drives.
Current code:
$Drives = Get-PSDrive -PSProvider 'FileSystem'
foreach($Drive in $Drives)
{
Get-ChildItem -Path $Drive.Root -include FindMe.txt -Recurse -ErrorAction SilentlyContinue | Invoke-Item
}
The goal is to save info like BIOS info in the found file.
But how can I grab the Path of the file or the found file itself to use it as a save file?
Thanks in advance
As commenter Lee_Dailey suggested, just assign the command output to a variable and use the FullName property of the FileInfo class to get the absolute file path.
$Drives = Get-PSDrive -PSProvider 'FileSystem'
$foundPath = foreach($Drive in $Drives)
{
# Find first file named FindMe.txt
$found = Get-ChildItem -Path $Drive.Root -Filter FindMe.txt -Recurse -ErrorAction SilentlyContinue |
Select-Object -First 1
if( $found ) {
# Output the full path of the found file.
# This is implicit output which PowerShell captures in $foundPath
$found.FullName
break
}
}
# Output found path to the console
$foundPath
Notes:
I've replaced -Include by -Filter which is more efficient because it is handled by the underlying .NET filesystem API. Using -Include the API would return all files and the filtering would be done by PowerShell, which is slower.
I've added Select-Object -First 1 to stop searching when the first file named 'FindMe.txt' has been found.
Remove | Select-Object -First 1 to get all files named 'FindMe.txt'. In this case, $found will be an array when more than one files are found. Also $found.FullName could resolve as an array due to member access enumeration.
I am creating a script where it searches through a bunch of nested folders to find the newest version of a software, which are in .msi form. My code currently can find the file and output it, but is not able to run the file.
I can use Select in the last line for the ForEach to output the correct file but when I change it to Start-Process, I get bombarded by errors.
$path="S:\\Releases\\Program"
$NoOfDirs=Get-ChildItem $path -Directory
ForEach($dir in $NoOfDirs){
Get-ChildItem "$path\$($dir.name)" -File -Recurse |
Where-Object {$_.LastWriteTime -gt ([DateTime]::Now.Adddays(-1))} |
Select-Object #{l='Folder';e={$dir.Name}},Name,LastWriteTime |
Sort-Object -pro LastWriteTime -Descending |
Start-Process -First 1
}
Is there a different command I should be using when running .msi files?
Since your code has to "search through a bunch of nested folders", I'd recommend using the -Recurse switch on Get-ChildItem.
Also use the -Filter parameter to have the search limited to .MSI files.
Something like this:
$path = "S:\Releases\Program"
$refDate = (Get-Date).Adddays(-1)
Get-ChildItem -Path $path -Filter '*.msi' -File -Recurse |
Where-Object {$_.LastWriteTime -gt $refDate} |
ForEach-Object {
# create the arguments for msiexec.exe.
# to find out more switches, open a commandbox and type msiexec /?
$msiArgs = '/i', ('"{0}"' -f $_.FullName), '/qn', '/norestart'
$exitCode = Start-Process 'msiexec.exe' -ArgumentList $msiArgs -NoNewWindow -Wait -PassThru
if ($exitCode -ne 0) {
# something went wrong. see http://www.msierrors.com/tag/msiexec-return-codes/
# to find out what the error was.
Write-Warning "MsiExec.exe returned error code $exitCode"
}
}
I have a bunch of lists of documents generated in powershell using this command:
Get-ChildItem -Recurse |
Select-String -Pattern "acrn164524" |
group Path |
select Name > test.txt
In this example it generates a list of files containing the string acrn164524 the output looks like this:
Name
----
C:\data\logo.eps
C:\data\invoice.docx
C:\data\special.docx
InputStream
C:\datanew\special.docx
I have been using
Get-Content "test.txt" | ForEach-Object {
Copy-Item -Path $_ -Destination "c:\destination\" -Recurse -Container -Force
}
However, this is an issue if two or more files have the same name and also throws a bunch of errors for any lines in the file that are not a path.
sorry if I was not clear enough I would like to keep files with the same name by appending something to the end of the file name.
You seem to want the files, not the output of Select-String. So let's keep the files.
Get-ChildItem -Recurse -File | Where-Object {
$_ | Select-String acrn164524 -Quiet
} | Select-Object -ExpandProperty FullName | Out-File test.txt
Here
-File will make Get-ChildItem only return actual files. Think
about using a filter like *.txt to reduce the workload more.
-Quiet will make Select-String return $true or $false, which
is perfect for Where-Object.
Instead of Select-Object -ExpandProperty X in order to retrieve an array of raw property values (as opposed to an array of PSObjects, which is what Select-Object would normally do), it's simpler to use ForEach-Object X instead.
Get-ChildItem -Recurse -File | Where-Object {
$_ | Select-String acrn164524 -Quiet
} | ForEach-Object FullName | Out-File test.txt
I have this PowerShell code that compares 2 directories and removes files if the files no longer exist in the source directory.
For example say I have Folder 1 & Folder 2. I want to compare Folder 1 with Folder 2, If a file doesn't exist anymore in Folder 1 it will remove it from Folder 2.
this code works ok but I have a problem where it also picks up file differences on the date/time. I only want it to pick up a difference if the file doesn't exist anymore in Folder 1.
Compare-Object $source $destination -Property Name -PassThru | Where-Object {$_.SideIndicator -eq "=>"} | % {
if(-not $_.FullName.PSIsContainer) {
UPDATE-LOG "File: $($_.FullName) has been removed from source"
Remove-Item -Path $_.FullName -Force -ErrorAction SilentlyContinue
}
}
Is there an extra Where-Object {$file1 <> $file2} or something like that.?
I am not sure how you are getting the information for $source and $destination I am assuming you are using Get-ChildItem
What i would do to eliminate the issue with date/time would be to not capture it in these variables. For Example:
$source = Get-ChildItem C:\temp\Folder1 -Recurse | select -ExpandProperty FullName
$destination = Get-ChildItem C:\temp\Folder2 -Recurse | select -ExpandProperty FullName
By doing this you only get the FullName Property for each object that is a child item not the date/time.
You would need to change some of the script after doing this for it to still work.
If I am not getting it wrong, the issue is your code is deleting the file with different time-stamp as compared to source:
Did you try -ExcludeProperty?
$source = Get-ChildItem "E:\New folder" -Recurse | select -ExcludeProperty Date
The following script can serve your purpose
$Item1=Get-ChildItem 'SourcePath'
$Item2=Get-ChildItem 'DestinationPath'
$DifferenceItem=Compare-Object $Item1 $Item2
$ItemToBeDeleted=$DifferenceItem | where {$_.SideIndicator -eq "=>" }
foreach ($item in $ItemToBeDeleted)
{
$FullPath=$item.InputObject.FullName
Remove-Item $FullPath -Force
}
Try something like this
In PowerShell V5:
$yourdir1="c:\temp"
$yourdir2="c:\temp2"
$filesnamedir1=(gci $yourdir1 -file).Name
gci $yourdir2 -file | where Name -notin $filesnamedir1| remove-item
In old PowerShell:
$yourdir1="c:\temp"
$yourdir2="c:\temp2"
$filesnamedir1=(gci $yourdir1 | where {$_.psiscontainer -eq $false}).Name
gci $yourdir2 | where {$_.psiscontainer -eq $false -and $_.Name -notin $filesnamedir1} | remove-item
If you want to compare files in multiple dir, use the -recurse option for every gci command.
I am trying to handle errors when scanning through folders. Let's say I have something like:
Get-ChildItem $somepath -Directory | ForEach-Object {
if(error occurred due to too long path) {
skip this folder then
} else {
Write-Host $_.BaseName
}
}
When I do this I print the folders in $somepath until one of them is too long and then the loop stops. Even when using SilentlyContinue. I want to print even after reaching a folder that is too long.
If you can install a non-ancient PowerShell version (3.0 or newer), simply prepend the path with \\?\ to overcome the 260-character limit for full path:
Get-ChildItem "\\?\$somepath" | ForEach {
# ............
}
You could try ignoring the files longer 260 characters by using the Where-Object cmdlet.
Get-ChildItem $somepath -Directory -ErrorAction SilentlyContinue `
| Where-Object {$_.length -lt 261} `
| ForEach-Object { Write-Host $_.BaseName }
Or you could use the following (Ref).
cmd /c dir $somepath /s /b | Where-Object {$_.length -lt 261}
I will add my solution since the neither on this page worked for me. I am using relative paths, so I can't use the \\ prefix.
$TestFiles = Get-ChildItem $pwd "*Tests.dll" -Recurse | Where-Object {$_.FullName.length -lt 261} | Select-Object FullName -ExpandProperty FullName | Where-Object FullName -like "*bin\Release*"
Write-Host "Found TestFiles:"
foreach ($TestFile in $TestFiles) {
Write-Host " $TestFile"
}