I have to find and then execute a .exe file from a script deployed by our asset management software. Currently it looks like this:
Set-Location $PSScriptRoot
$proc = (Start-Process -FilePath "C:\Program Files (x86)\software\software name\Uninstall.exe" -ArgumentList "/S /qn" -Wait -PassThru)
$proc.WaitForExit()
$ExitCode = $proc.ExitCode
Exit($ExitCode)
As far as I understand the location for the location for the file is set and some users do not have it there hence why it fails.
So I understand that you can search for a program with
Get-ChildItem C:\Program Files (x86)\software\
And execute with Start-process -Filepath
But do I simply combine that with a | or is there an easier way/will it even work.
As commenter suggested, you can use Test-Path to test if a path exists:
$uninstallPath = Join-Path ${env:ProgramFiles(x86)} 'software\software name\Uninstall.exe'
if( Test-Path $uninstallPath ) {
$proc = Start-Process -FilePath $uninstallPath -ArgumentList '/S /qn' -Wait -PassThru
$proc.WaitForExit()
$ExitCode = $proc.ExitCode
Exit $ExitCode
}
I've also made the code more robust by avoiding the hardcoded "Program Files (x86)" directory, using an environment variable. Because of the parentheses in the name of the env var, it must be enclosed in curly braces.
For added robustness, you may read the path of the uninstall program from the registry, as detailed by this Q&A. If you are lucky, the program even stores a QuietUninstallString in the registry, which gives you the full command line for silent uninstall.
Related
I've got 4,000 shares to go through with the readinessreportcreator, which works when I run it locally, however I put together a command with a foreach loop to read a csv of all the DFS shares, however I need to go deeper into the folder structure, normally I would use the -recurse switch, but that doesn't seem to be working.
The site I got the command from
https://learn.microsoft.com/en-us/deployoffice/use-the-readiness-toolkit-to-assess-application-compatibility-for-office-365-pro
The powershell I have put together is:
$shares = import-csv -path 'C:\temp\dfs.csv'
$shs = ($shares).share
foreach ($sh in $shs)
{
Start-Process -NoNewWindow -FilePath "C:\Program Files (x86)\Microsoft Readiness Toolkit for Office\ReadinessReportCreator.exe" "$sh" Out-File "C:\temp"
write-host "share=" $sh
}
The command the site suggest from a command line is:
ReadinessReportCreator.exe -p c:\officefiles\ -r -output \\server01\finance
I was thinking if I could just use the foreach loop and change c:\officefiles\ with the variable of the share it would run through each folder and subfolder, but it doesn't like the -p, -r or the -output. Porbably because they're not powershell cmdlets, so -p should be -path and -r should be -recurse and -output should be out-file, but onlu out-file is recognised.
Excel file si:
share
\\vmshare\share
\\vm3share\share1
\\vm2share\share2
\\vm2share\share3
Hope this makes sense
Thanks in advance
1)Info from site above:
ReadinessReportCreator.exe -p c:\officefiles\ -r -output \\server01\finance -silent
The following is an example of a command line that you can run to scan a folder, and all its subfolders, and save the results to a network share for the Finance department. This only scans for VBA macros.
2)Can't see structure your csv file, but i have doubt about this rows of code(not sure what in $shs):
$shares = import-csv -path 'C:\temp\dfs.csv'
$shs = ($shares).share
3)When you run command Start-Process you must pass parameters via key -ArgumentList,
-Wait — Wait for the process to be finished before accepting any more inputs.
Start-Process .\DeploymentServer.UI.CommandLine.exe -ArgumentList "register --estNumber $Number --postcode `"$PostCode`" --password $Password" -Wait -NoNewWindow
4)For more about Start-Process enter in cli Get-Help Start-Process -Online
I have a cute little script in my $PROFILE helping me start Notepad++ from the script showing a file of my choosing.
function Edit{
param([string]$file = " ")
Start-Process "C:\Program Files\Notepad++\notepad++.exe" -ArgumentList $file
}
It's worked great until recently, where I jump between different systems. I discovered that NPP is installed in C:\Program Files on some systems but in C:\Program Files (x86) on others. I can edit the script adapting it but having done so a gazillion times (i.e. 5 to this point), I got sick and tired of it, realizing that I have to automate this insanity.
Knowing little about scripting, I wonder what I should Google for. Does best practice dictate using exception handling in such a case or is it more appropriate to go for conditional expressions?
According to Get-Host | Select-Object Version I'm running version 5.1, if it's of any significance. Perhaps there's an even neater method I'm unaware of? Relying on an environment variable? I'd also prefer to not use a method valid in an older version of PS, although working, if there's a more convenient approach in a later one. (And given my experience on the subject, I can't tell a duck from a goose.)
I would use conditionals for this one.
One option is to test the path directly if you know for certain it is in a particular location.
Hard coded paths:
function Edit{
param([string]$file = " ")
$32bit = "C:\Program Files (x86)\Notepad++\notepad++.exe"
$64bit = "C:\Program Files\Notepad++\notepad++.exe"
if (Test-Path $32bit) {Start-Process -FilePath $32bit -ArgumentList $file}
elseif (Test-Path $64bit) {Start-Process -FilePath $64bit -ArgumentList $file}
else {Write-Error -Exception "NotePad++ not found."}
}
Another option is pulling path information from registry keys, if they're available:
function Edit{
param([string]$file = " ")
$32bit = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Notepad++\' -ErrorAction SilentlyContinue).("(default)")
$64bit = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\WOW6432Node\Notepad++\' -ErrorAction SilentlyContinue).("(default)")
if ($32bit) {Start-Process -FilePath "$32bit\notepad++.exe" -ArgumentList $file}
elseif ($64bit) {Start-Process -FilePath "$64bit\notepad++.exe" -ArgumentList $file}
else {Write-Error -Exception "NotePad++ not found."}
}
Based on the great help from #BoogaRoo (who should get some +1 for effort) and asked by the same to post my own version of the answer, I go against my reluctance to post asnwers to own questions due to strong sensation of tackiness.
My final version, taking into account systems that lack NP++ but still want to show the editor of some kind.
function Edit{
param([string]$file = " ")
$executable = "Notepad++\notepad++.exe"
$32bit = "C:\Program Files (x86)\" + $executable
$64bit = "C:\Program Files\" + $executable
$target = "notepad"
if(Test-Path $32bit) { $target = $32bit }
if(Test-Path $64bit) { $target = $64bit }
Start-Process $target -ArgumentList $file
}
Let me offer a streamlined version that also supports passing multiple files:
function Edit {
param(
# Allow passing multiple files, both with explicit array syntax (`,`-separated)
# or as indiv. arguments.
[Parameter(ValueFromRemainingArguments)]
[string[]] $File
)
# Construct the potential Notepad++ paths.
# Note: `-replace '$'` is a trick to append a string to each element
# of an array.
$exePaths = $env:ProgramFiles, ${env:ProgramFiles(x86)} -replace '$', '\Notepad++\notepad++.exe'
# See which one, if any, exists, using Get-Command.
$exeToUse = Get-Command -ErrorAction Ignore $exePaths | Select-Object -First 1
# Fall back to Notepad.
if (-not $exeToUse) { $exeToUse = 'notepad.exe' }
# Invoke whatever editor was found with the optional file(s).
# Note that both Notepad++ and NotePad can be invoked directly
# without blocking subsequent commands, so there is no need for `Start-Process`,
# whose argument processing is buggy.
& $exeToUse $File
}
An array of potential executable paths is passed to Get-Command, which returns a command-info object for each actual executable found, if any.
-ErrorAction Ignore quietly ignores any errors.
Select-Object -First 1 extracts the first command-info object, if present, from the Get-Command output; this is necessary to guard against the (perhaps unlikely) case where the executable exists in both locations.
$exeToUse receives $null (effectively) if Get-Command produces no output, in which case Boolean expression -not $exeToUse evaluates to $true, causing the fallback to notepad.exe to take effect.
Both command names (strings) and command-info objects (instances of System.Management.Automation.CommandInfo or derived classes, as returned by Get-Command) can be executed via &, the call operator.
This question already has answers here:
How can you use an object's property in a double-quoted string?
(5 answers)
Closed 3 years ago.
I'm trying to write a script in PowerShell that will search a folder for a specific msi file (subfolder location will vary) and then run an install command referencing that file and other files that would be in the same directory. It would be for a tool that our L1 helpdesk staff could use to run an install for software assigned through SCCM, possibly using different command line variables than what SCCM would run.
Tried different combinations of code and can't install the app.
This works fine on finding the software
Get-ChildItem -Path C:\Windows\ccmcache -Recurse -Filter softwarename.msi
ForEach portion does not work
$Path = Get-ChildItem -Path C:\Windows\CCMCache -Recurse -Filter Something.MSI
ForEach ( $Installer in ( Get-ChildItem -Path $Path.DirectoryName -Filter *.MSI ) ) {
Start-Process -Wait -FilePath C:\windows\system32\msiexec.exe -ArgumentList "/i $Installer.FullName"
}
I basically want to do something like this, using the directory the msi file was found in to do the install:
msiexec /i softwarename.msi /q /norestart
The only thing I see wrong is you can't normally refer to properties of an object inside of a string without adding $( ):
Start-Process -Wait -FilePath C:\windows\system32\msiexec.exe -ArgumentList "/i $($Installer.FullName)"
Why not run msiexec directly?
msiexec /i $installer.fullname
MSI Generator - Downloads custom-named MSI, and I need to download and execute.
I can download and specify a saved file name, but I can't get passed that.
So, this special URL creats a custom MSI file to download, e.g. Installer_t493ht.msi (the random part of that changes each time). I'm trying to use PowerShell to download this file to a specific directory, assign the file name to a variable, and then execute.
What I have so far is:
$url = "https://UrlThatGeneratesRandomlyNamedMSI"
$output = "c:\SaveToThisDirectory\"
Import-Module BitsTransfer
$job += Start-BitsTransfer -Source $url -Destination $output
while ($job | Where-Object {$job.JobState -eq "Transferring"}) {
Sleep -Seconds 1
}
Start-Process msiexec.exe -Wait -ArgumentList '/I /qn $output/$RandomName.msi REBOOT=ReallySuppress'
Just add something like:
$msiFile = Get-Item -Path "$output\Installer_*.msi"
Than execute that (make sure you use double quotes, so the variabele gets expanded):
Start-Process msiexec.exe -Wait -ArgumentList "/I /qn $($msiFile.FullName) REBOOT=ReallySuppress"
i would like to copy the license folder and overwrite the existing folder, since it is program file (x86), i have to run the elevated powershell, i am able to copy it when i launch it manually, just wonder is it possible to get all run at one line (all at once) ? really appreicated
$net = new-object -ComObject WScript.Network
$net.MapNetworkDrive("R:", "\\roa\smdd\Software\Mest", $false)
Start-process Powershell.exe -ArgumentList " Copy-Item "R:\Licenses\" "C:\Program Files `(x86`)\Mest Research S.L\Mest\licenses"" -force -recurse -wait
You don't need to map a drive or invoke powershell.exe. The code is PowerShell, so you don't need to spin up a new copy of PowerShell to run the Copy-Item cmdlet to copy files. You only need one PowerShell command:
Copy-Item "\\roa\smdd\Software\Mest\Licenses\*" "${Env:ProgramFiles(x86)}\Mest Research S.L\Mest\licenses" -Force -Recurse
Note that you will likely need to open PowerShell as administrator (elevated) to be able to copy items into that directory.