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"
}
}
Related
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
}
I have created a script that compress files older than N days using PowerShell.
Like this:
param (
$dirPath, `
[int] $daysAgo, `
$logOutput=$dirPath+"\old_reports.log", `
$fileExt
)
$curDate = Get-Date
$timeAgo = ($curDate).AddDays($daysAgo)
$files = Get-ChildItem -Recurse `
-Path $dirPath `
-Include *.$fileExt| `
Where-Object { $_.LastWriteTime -lt $timeAgo } | `
Select -ExpandProperty FullName
& 'C:\Program Files\7-Zip\7Z.exe' a -t7z -mx9 old_reports.7z $files -bb1 -sdel
echo $files > $logOutput
It is working, but, since there are many files, it takes a while to fill the $files variable. While it is doing that, the prompt shows only a blinking cursor. Therefore, I am not aware if the script is actually doing something or it's paused by an accidental click.
Is there a way to show that $files variable is receiving input?
Without restructuring your command - and thereby sacrificing performance - I see only one option:
In addition to capturing file-info objects in variable $files, print them to the display as well, which you can do with the help of the common -OutVariable parameter:
# Output the files of interest *and* capture them in
# variable $files, via -OutVariable
Get-ChildItem -Recurse `
-Path $dirPath `
-Include *.$fileExt| `
Where-Object { $_.LastWriteTime -lt $timeAgo } | `
Select -ExpandProperty FullName -OutVariable files
Power shell script (delete old backup files)
$date = (get-date).AddDays(-10)
$files = Get-ChildItem -Path "\\\sc-sqlbackups\sqlbackups\SA144\Milli\teste" -Files | ?{$_.LastWriteTime -lt $date}
foreach ($file in $files) {
Write-Host "Removing $file"
$file | Remove-Item -ErrorAction SilentlyContinue
}
the above command runs without an issue in Windows powershell(Windows 10). But when I create a SQL Server job with the same command I get the
ERROR
A job step received an error at line 2 in a PowerShell script.
The corresponding line is
'$files = Get-ChildItem -Path "\\sc-sqlbackups\sqlbackups\SA144\Milli\teste" -File | ?{$_.LastWriteTime -lt $date} '. Correct the script and reschedule the job. The error information returned by PowerShell is: 'A parameter cannot be found that matches parameter name 'File'.
-Files is not support in old PS Verisons.
use Where-Object { $_.PSIsContainer -eq $false } Instead!
I am wondering if there is better way to make a script on PowerShell these instructions:
Search on 3 paths. Ex.
$LOGDIRS="C:\NETiKA\GED\Production\RI\log";"C:\NETiKA\GED\Test\RI\log";"C:\NETiKA\Tomcat-8.0.28\logs"
Find all files that are older than 7 days and copy on a file that I will call file.list . EX. > C:\Test\file.list
When I copied on my file.list, I need to search all the name of the files and delete them.
Apparently when you have more than thousands of file, this is the
fastest way to delete.
$LOGDIRS=C:/NETiKA/GED/Production/RI/log;C:/NETiKA/GED/Test/RI/log;C:/NETiKA/Tomcat-8.0.28/logs
$KEEP=-7
Get-ChildItem -Path $LOGDIRS -Recurse -Directory -Force -ErrorAction SilentlyContinue |
Select-Object FullName > files.list |
Foreach-Object {
if ($_.LastAccessTime -le (get-date).adddays($KEEP)) {
remove-item -recurse -force $_
}
};
Something like this should help you get started.
$path1 = "E:\Code\powershell\myPS\2018\Jun"
$path2 = "E:\Code\powershell\myPS\2018\Jun\compareTextFiles"
$path3 = "E:\Code\powershell\myPS\2018\May"
$allFiles = dir $path1, $path2, $path3 -File
$fileList = New-Item -type file file.list -Force
$keep = -7
$allFiles | foreach {
if ($_.LastAccessTime -le (Get-Date).AddDays($keep)) {
"$($_.FullName) is older than 7 days"
$_.FullName.ToString() | Out-File $fileList -Append
}
else {
"$($_.FullName) is new"
}
}
You can add deletion in the code in IF Block if you wish or check the file and do it later on. Your code has many issues which are very basic to PowerShell, e.g: once you use Select-Object the next pipeline will only receive the property you selected. You have tried using LastAccessTime in later pipe when you only selected to go ahead with FullName property.
Also, redirecting to a file and again using pipeline looks very messy.
Remove-Item accepts piped input and a
Where will filter the age
to first check what would be deleted I appended a -WhatIf to the Remove-Item
$LOGDIRS="C:\NETiKA\GED\Production\RI\log","C:\NETiKA\GED\Test\RI\log","C:\NETiKA\Tomcat-8.0.28\logs"
$KEEP=-7
Get-ChildItem -Path $LOGDIRS -Recurse -Directory -Force -ErrorAction SilentlyContinue |
Where-Object LastAccessTime -le ((get-date).AddDays($KEEP))
Remove-Item -Recurse -Force $_ -Whatif
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"
}