Scripts executed by a WinRar self-extracting archive not behaving as expected - powershell

I have a self-extracting WinRar archive set up to run a powershell script upon completion. The script will launch, but specific commands do not give expected results.
In particular, I have the following command to find the installation path of an installed game (Risk of Rain 2).
Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall | % { Get-ItemProperty $_.PsPath } | Where-Object {$_.DisplayName -like 'Risk of Rain 2'} | Select InstallLocation -ExpandProperty InstallLocation
When running the script by itself, I get the install path as expected.
F:\SteamLibrary\steamapps\common\Risk of Rain 2
When the script is launched (either before or after extraction), the command seems to run, but outputs nothing.
In testing, I removed everything but the following:
Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall | % { Get-ItemProperty $_.PsPath }
This still works, outputting a list of installed applications. When I add the pipe to the "Where-Object" portion, it starts to fail.
My only guess is that WinRar is starting the scripts with some other parameters set.
I tried having Winrar start a .bat that will then run the .ps1 file, but had the same result. Same goes for running the archive as an administrator.
Is something funky with my powershell script, or am I just missing something with how Winrar handles things?
Thanks!

So according to your comment the archive uses underscore in place of spaces. So looking at your code we see this originally
Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall | % { Get-ItemProperty $.PsPath } | Where-Object {$.DisplayName -like 'Risk of Rain 2'} | Select InstallLocation -ExpandProperty InstallLocation
Your error occurs when we try to include the Where-object. So I believe the solution to your problem should be to modify your code as such:
Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall | % { Get-ItemProperty $_.PsPath } | Where-Object {$_.DisplayName -like 'Risk_of_Rain_2'} | Select InstallLocation -ExpandProperty InstallLocation
Essentially we changed the -like from 'Risk of Rain 2' to 'Risk_of_Rain_2'
I believe this should solve the problem as it is called by the WinRar it probably still uses the underscore name. Since you are checking for a like name that only indicates that it can include any number and type of characters after or before the words in the like string. The like string itself is exact.

So...WinRAR gives options for the SFX module used. I was using the default Zip.SFX module when I should have been using Zip64.SFX.
The powershell session it opened previously was 32-bit, which gives a different result when reading the installed programs from the registry.
If you have anything 32/64 bit specific in the commands being run after an extraction, make sure the right module is selected in the Advanced SFX options.
Thank you all for the help!
Advanced SFX options

Related

is there a way to check if a program is installed just by filename in powershell?

I am trying to create a powershell script to auto install all .msi and .exe files silently in a directory. However while doing this i want to check if any of the programs are already installed.
I know i can get all installed files in the system like below
$32bit_softwares = Get-ItemProperty HKLM:\SOFTWARE\wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* |
Select-Object DisplayName,DisplayVersion,Publisher,InstallDate
$64bit_softwares = Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\* |
Select-Object DisplayName,DisplayVersion,Publisher,InstallDate
$all_softwares = $32bit_softwares+$64bit_softwares
and i can get the filenames of the files in the directory like below:
$directoryRead = Read-Host -Prompt "enter directory"
$fileNames = Get-ChildItem $directory -Recurse -include *.exe,*.msi | ForEach-Object {$_.name}
How can i compare these 2 in a loop? like
$all_softwares.DisplayName -like "$softwareName*"
I am not sure a like filter above will do the job as filenames will be like examplename.exe
Thanks in advance.
So the problem that I see and I think you are asking about is that the installer filename will be different than the software name you pull out of the Registry. With the difference it will be hard to match up exactly.
Is the set of MSI's and/or EXE's (the installers) a known, static set? Would it be possible to setup a hash table (dictionary) mapping between the Registry name and the installer name?
This would make matching exactly while looping through the installers and doing a .Contains on the array from the Registry easier.

How to use Powershell Invoke-Item with a gci search?

I'm trying to build a Powershell script that uses the Invoke-Item cmdlet once I've found the files using the following script:
gci -Recurse -File *.xls* C:\folder_example\'  |  sort LastWriteTime | Select -last 2 | 
I'm not sure how best to implement the Invoke-Item cmdlet from here?
In its simplest usage, Invoke-Item (aliased as ii) just needs a path given to it either through pipeline or through a parameter.
In this case, it could be as simple as adding | Invoke-Item to the end of your command:
gci -Recurse -File *.xls* C:\folder_example\ | sort LastWriteTime | select -Last 2 | Invoke-Item
One thing I will mention, is that I have found that certain programs cannot handle a bunch of files thrown at them in quick succession like this script will do.
I usually run into this when trying to open a bunch of files in Notepad++.
I'm going to simplify the command a bit just to keep it short...but usually one of these two changes will fix my problem:
Get-ChildItem -Filter *.xls* | ForEach-Object { Invoke-Item $_.FullName }
Placing the call into a ForEach-Object script block will sometimes fix this. I'm not 100% why this fixes it, I have some theories but that's all they are, so I will keep them out of this answer. But it always fixes the issue for me when using Invoke-Item on Notepad++ files.
Some programs, like Excel, have a start up splash screen and some lag before they eventually open. So you could always throw a sleep in there just to help slow things down a bit in case you are still having trouble:
Get-ChildItem -Filter *.xls* | ForEach-Object { Start-Sleep 0.1; Invoke-Item $_.FullName }
That will add a small 100ms pause just to give whatever application you are running to have a little time to process.

Azure Devops - PowerShell task on self-hosted agent

I have an Azure Devops deployment pipeline setup which is building and I am able to deploy to a self hosted virtual machine with no issue.
I have the following powershell script that correctly clears down my destination directory leaving 2 folders that are not part of source control
Get-ChildItem -Path 'C:\inetpub\wwwroot\testDeploy\' -Recurse -exclude "pod","photos" |
Select -ExpandProperty FullName |
Where {$_ -notlike '*\pod\*' -and $_ -notlike '*\photos\*'} |
sort length -Descending |
Remove-Item -force
I have tried adding a "PowerShell Script" task but i'm don;t know how to get the PowerShell script in to a folder that the task can access i.e. $(System.DefaultWorkingDirectory). Can anyone advise how I should be either generating the file or where to store it in my repo that is then accessible by the self-hosted Windows agent
Agree with Shayki, you can create a powershell(.ps1) file in repos and paste your script in it to achieve that. And then, use powershell task to execute the script which in ps1 file.
But, as you said that you want it be maintained within the repos easily. Need made some change on your script:
Param(
[string]$RootPath,
[string]$File1,
[string]$File2,
[string]$NonLike1,
[string]$NonLike2
)
Get-ChildItem -Path $RootPath -Recurse -include $File1,$File2 |
Select -ExpandProperty FullName |
Where {$_ -notlike $NonLike1 -and $_ -notlike $NonLike2} |
sort length -Descending |
Remove-Item -Recurse -force
The first change is, you need to replace the hard code with variable. Pass the value with task, this is a good way to maintain your script.
The second which also the important change is add -Recurse after Remove-Item, or you will get the error showed below while the value of $RootPath is hard code, such as 'C:\Users\'.
Remove-Item : Windows PowerShell is in NonInteractive mode. Read and
Prompt functionality is not available.
And then, you can add task in your build pipeline. Add the Script path where the .ps1 file located and input the Arguments with the value:
If you want to access $(System.DefaultWorkingDirectory), pass it to $RootPath.
Hope my sample can help you achieve what you want.

Powershell close multiple windows / end processes based on name

I have a pretty neat mess of batch/python scripts that install a program called MATRIS, followed by about 15 exe updates.
Around 11 of these updates open a window telling me the that the update was successful.
Now it would be really fun to run a batch or powershell script which closes all of these windows for me.
The last thing I tried was Get-Process | Where-Object {$_.Path -like "MatrisInstaller.APCIPLUS"} | Stop-Process -WhatIf
I wasn't sure if it was the name as read in task manager, or like the title of the window, but I tried both.
Please note that a couple of these are (32 bit) - I'm not sure if that would impact the script.
I was able to run tasklist followed by kill {PID} but PIDs change: I'm not sure how to script it.
Please reply if you need any clarification, I've historically been poor at wording my questions.
In your example, Path is pointing to the executable file on disk, so while possible to use (if it is consistent), it won't match the name you find in the processes tab of Task Manager. Typically, people will use the name as shown on the Details tab of Task manager. For example, with Outlook on my system, these three possibilities are:
Path: C:\Program Files\Microsoft Office\Office16\OUTLOOK.EXE
Processes tab: Microsoft Outlook
Details tab: outlook.exe
So, you need a command like this:
Get-Process | Where Name -eq 'Outlook' | Stop-Process
or, better:
Get-Process -Name 'Outlook' | Stop-Process
Note that PowerShell expects you to remove the '.exe' you see in Task manager.
EDIT: Additional technique
If you know the names of the processes, then you can simplify your script by doing something like this:
$processList = "Process1","Process2","Process3" # Add all process names to the list
$processList |
ForEach-Object {
Get-Process -Name $_ | Stop-Process
}
You were almost there, just need to change "Path" to "ProcessName" like so:
Get-Process | Where-Object {$_.ProcessName -like "MatrisInstaller.APCIPLUS"} | Stop-Process -WhatIf

get the latest created folder from a path using powershell

How to get the latest created folder from a path using Windows PowerShell?
I have the path C:\temp and I want to find the most recently created folder in this path.
PowerShell works mainly with the pipeline, so most of what you'd write will consist of creating objects representing some information, and filtering and manipulating them. In this case, the objects are a bunch of folders.
Get all items in the folder. This will get files and folders, that's why step 2 is necessary. The | at the end of the line signals that the pipeline will continue in the next line – objects created by Get-ChildItem will then be passed one by one to another command.
Get-ChildItem c:\temp |
Filter for folders. There is no really elegant way, sadly. Don't worry about that it says “container”, not “folder” – Those commands work with many different things, not only files and folders, so a more general concept was used in naming.
Where { $_.PSIsContainer } |
Sort by date, descending, so the newest folder is the first one.
Sort CreationTime -Descending |
Select the first (newest) folder.
Select -First 1
So in short:
gci c:\temp | ? { $_.PSIsContainer } | sort CreationTime -desc | select -f 1
or
(gci c:\temp | ? { $_.PSIsContainer } | sort CreationTime)[-1]
Both of those lines make heavy use of default aliases for commands in PowerShell, such as ? for Where-Object. You should use the full names in scripts, though, as you'll never know what the aliases will look like on other machines the code might run on.
EDIT: PowerShell 3 has additional parameters for Get-ChildItem that allow you to do filtering for files or folders directly, so you don't need the Where:
Get-ChildItem -Directory C:\temp | ...
Generally you will work with objects and their properties in PowerShell. Two very helpful commands are Get-Member and its alias gm and Get-Command or just gcm. Get-Member will tell you what properties and methods an object has; you just pipe something else into it for that:
Get-ChildItem | gm
will tell you what properties files and directories have.
Get-Command will list all commands there are or those that match a particular pattern. PowerShell commands try to be very consistent in their use of verbs and nouns. To find all commands that end in Object you can try gcm *-Object – those are general commands working with pretty much everything. Get-Help ForEach-Object then would tell you about a particular command, ForEach-Object in this case.