Why does this work (or how)? - variables

In my email today I received an email about getting unused drive letters. This was their solution:
Get-ChildItem function:[d-z]: -Name | Where-Object {-not (Test-Path -Path $_)}
PowerShell Magazine BrainTeaser had this for a solution, same thing.
ls function:[d-z]: -n|?{!(test-path $_)}|random
I have no idea how function:[d-z]: works. I know that for each character between 'd' to 'z' is used but I don't know why the syntax works.
Testing Get-ChildItem function:[d-a]: -Name gives you an error saying Get-ChildItem : Cannot retrieve the dynamic parameters for the cmdlet. The specified wildcard pattern is not valid:[d-a]:
So is that a dynamic parameter? How come is does not show up with Get-Help gci -full?

function: is a PSDrive which exposes the set of functions defined in the current session. PowerShell creates a function for each single letter drive, named as the letter followed by a colon.
So, function:[d-z]: lists the functions from "d:" through "z:"
function:[d-a]: doesn't work because , d-a isn't a range of letters.

Related

Powershell - Comparing LineNumbers of lines that match

I'm having trouble to come up with solution that would compare LineNumbers of matching pairs from two lists. I will show you what I mean on example.
I have one SQL script, where I am inserting some data into existing tables. For ensuring repeatability of the script, before every insert into I am deleting the previous content of the table with "delete" statement. I am able to parse the file and check If every "insert into database1.table1" also have "delete from database1.table1" in the file. But i don't know how to check if the delete statement of the particular table is before the insert into statement (you need to delete the content of the table before you load new data into it). I figured I would need to use the LineNumber property, but I really don't know how to combine it with the database.table check.
This is what i got into first variable with this command:
$insertinto = Get-ChildItem "$packagepath\Init\" -Include 03_Init_*.txt -Recurse | Select-String -Pattern "insert into "
#content of variable
C:\Users\hanus\Documents\sql_init.txt:42:insert into database1.table1
C:\Users\hanus\Documents\sql_init.txt:130:insert into database1.table2
C:\Users\hanus\Documents\sql_init.txt:282:insert into database2.table3
Here is what I got into second variable with this command:
$deletefrom = Get-ChildItem "$packagepath\Init\" -Include 03_Init_*.txt -Recurse | Select-String -Pattern "delete from "
#content of the variable
C:\Users\hanus\Documents\sql_init.txt:40:delete from database1.table1;
C:\Users\hanus\Documents\sql_init.txt:128:delete from database1.table2;
C:\Users\hanus\Documents\sql_init.txt:280:delete from database2.table3;
The expected output would be something like: This"delete from" statement is not before "insert into" statement, even though it's in the file.
I hope I described the problem well. I am new to Powershell and scripting so be please patient with me. Thank you for any help in advance!
You're already using Select-String, so this should be pretty simple. The content of those variables is far more than you're seeing there. Run this:
$deletefrom | Format-List * -Force
You'll see that each match contains an object with properties for what file the match is from, what line number the match was found on, and more. I think if you capture the table that is being modified in your Select-String with a look behind of what you're searching on now you could group on that, and then alert on times where the delete happens after the insert.
Get-ChildItem "$packagepath\Init\*" -Include 03_Init_*.txt -Recurse |
Select-String "(?<=delete from |insert into )([^;]+)" |
Group-Object {$_.Matches[0].value} |
ForEach-Object {
if($_.group[0] -notmatch 'delete from'){Write-Warning "Inserting into $($_.Name) before deleting"}
}

run powershell in VBA access

I need to change a string in multiple text files
I have written the script below in ACCESS VBA but the error is TYPE MISMATCH
Dim str As String
str = "N=maher"
Call Shell("c:\windows\system32\powershell.exe" - Command("get-content -Path e:\temptest.txt") - Replace(str, "maher", "ali"))
The syntax for calling PowerShell is way off. Suggestion: get it working from the command line yourself first, and then run from Access (an odd choice: it just makes this more complicated).
A PowerShell script to do this (.ps1 file) would need to contain something like:
Get-Content -Path "E:\temptest.txt" | ForEach-Object { $_ -Replace 'maher', 'ali' } | do-something-with-the-updated-content
You need to define:
What you are replacing (you pass N=maher in but then hard code two strings for Replace.
What do to with the strings after doing the replacement (Get-Content only reads files).

Powershell - listing folders in mulitple places and changing files in those places

I'm trying to set up a script designed to change a bit over 100 placeholders in probably some 50 files. In general I got a list of possible placeholders, and their values. I got some applications that have exe.config files as well as ini files. These applications are stored in c:\programfiles(x86)\ and in d:\In general I managed to make it work with one path, but not with two. I could easily write the code to replace twice, but that leaves me with a lot of messy code and would be harder for others to read.
ls c:\programfiles(x86) -Recurse | where-object {$_.Extension -eq ".config" -or $_.Extension -eq ".ini"} | %{(gc $PSPath) | %{
$_ -replace "abc", "qwe" `
-replace "lkj", "hgs" `
-replace "hfd", "fgd"
} | sc $_PSPath; Write-Host "Processed: " + $_.Fullname}
I've tried to include 2 paths by putting $a = path1, $b = path2, c$ = $a + $b and that seems to work as far as getting the ls command to run in two different places. however, it does not seem to store the path the files are in, and so it will try to replace the filenames it has found in the folder you are currently running the script from. And thus, even if I might be in one of the places where the files is supposed to be, it's not in the other ...
So .. Any idea how I can get Powershell to list files in 2 different places and replace the same variables in both places without haveing to have the code twice ? I thought about putting the code I would have to use twice into a variable, calling it when I needed to instead of writing it again, but it seemed to resolve the code before using it, and that didn't exactly give me results since the data comes from the first part.
If you got a cool pipeline, then every problem looks like ... uhm ... fluids? objects? I have no clue. But anyway, just add another layer (and fix a few problems along the way):
$places = 'C:\Program Files (x86)', 'D:\some other location'
$places |
Get-ChildItem -Recurse -Include *.ini,*.config |
ForEach-Object {
(Get-Content $_) -replace 'abc', 'qwe' `
-replace 'lkj', 'hgs' `
-replace 'hfd', 'fgd' |
Set-Content $_
'Processed: {0}' -f $_.FullName
}
Notable changes:
Just iterate over the list of folders to crawl as the first step.
Doing the filtering directly in Get-ChildItem makes it faster and saves the Where-Object.
-replace can be applied directly to an array, no need for another ForEach-Object there.
If the number of replacements is large you may consider using a hashtable to store them so that you don't have twenty lines of -replace 'foo', 'bar'.

How do I get the value of a registry key and ONLY the value using powershell

Can anyone help me pull the value of a registry key and place it into a variable in PowerShell? So far I have used Get-ItemProperty and reg query and although both will pull the value, both also add extra text. I need just the string text from the registry key and ONLY the string text from the key. I'm sure I could create a function to strip off the extra text but if something changes (i.e. reg key name) it might affect this.
$key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion'
(Get-ItemProperty -Path $key -Name ProgramFilesDir).ProgramFilesDir
I've never liked how this was provider was implemented like this : /
Basically, it makes every registry value a PSCustomObject object with PsPath, PsParentPath, PsChildname, PSDrive and PSProvider properties and then a property for its actual value. So even though you asked for the item by name, to get its value you have to use the name once more.
NONE of these answers work for situations where the value name contains spaces, dots, or other characters that are reserved in PowerShell. In that case you have to wrap the name in double quotes as per http://blog.danskingdom.com/accessing-powershell-variables-with-periods-in-their-name/ - for example:
PS> Get-ItemProperty Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7
14.0 : C:\Program Files (x86)\Microsoft Visual Studio 14.0\
12.0 : C:\Program Files (x86)\Microsoft Visual Studio 12.0\
11.0 : C:\Program Files (x86)\Microsoft Visual Studio 11.0\
15.0 : C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\V
S7
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS
PSChildName : VS7
PSProvider : Microsoft.PowerShell.Core\Registry
If you want to access any of the 14.0, 12.0, 11.0, 15.0 values, the solution from the accepted answer will not work - you will get no output:
PS> (Get-ItemProperty Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7 -Name 15.0).15.0
PS>
What does work is quoting the value name, which you should probably be doing anyway for safety:
PS> (Get-ItemProperty "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7" -Name "15.0")."15.0"
C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\
PS>
Thus, the accepted answer should be modified as such:
PS> $key = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7"
PS> $value = "15.0"
PS> (Get-ItemProperty -Path $key -Name $value).$value
C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\
PS>
This works in PowerShell 2.0 through 5.0 (although you should probably be using Get-ItemPropertyValue in v5).
Harry Martyrossian mentions in a comment on his own answer that the
Get-ItemPropertyValue cmdlet was introduced in Powershell v5, which solves the problem:
PS> Get-ItemPropertyValue 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion' 'ProgramFilesDir'
C:\Program Files
Alternatives for PowerShell v4-:
Here's an attempt to retain the efficiency while eliminating the need for repetition of the value name, which, however, is still a little cumbersome:
& {
(Get-ItemProperty `
-LiteralPath HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion `
-Name $args
).$args
} ProgramFilesDir
By using a script block, the value name can be passed in once as a parameter, and the parameter variable ($args) can then simply be used twice inside the block.
Alternatively, a simple helper function can ease the pain:
function Get-RegValue([String] $KeyPath, [String] $ValueName) {
(Get-ItemProperty -LiteralPath $KeyPath -Name $ValueName).$ValueName
}
Note: All solutions above bypass the problem described in Ian Kemp's's answer - the need to use explicit quoting for certain value names when used as property names; e.g., .'15.0' - because the value names are passed as parameters and property access happens via a variable; e.g., .$ValueName
As for the other answers:
Andy Arismendi's helpful answer explains the annoyance with having to repeat the value name in order to get the value data efficiently.
M Jeremy Carter's helpful answer is more convenient, but can be a performance pitfall for keys with a large number of values, because an object with a large number of properties must be constructed.
I'm not sure if this has been changed, or if it has something to do with which version of PS you're using, but using Andy's example, I can remove the -Name parameter and I still get the value of the reg item:
PS C:\> $key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion'
PS C:\> (Get-ItemProperty -Path $key).ProgramFilesDir
C:\Program Files
PS C:\> $psversiontable.psversion
Major Minor Build Revision
----- ----- ----- --------
2 0 -1 -1
Given a key \SQL with two properties:
I'd grab the "MSSQLSERVER" one with the following in-cases where I wasn't sure what the property name was going to be to use dot-notation:
$regkey_property_name = 'MSSQLSERVER'
$regkey = get-item -Path 'HKLM:\Software\Microsoft\Microsoft SQL Server\Instance Names\SQL'
$regkey.GetValue($regkey_property_name)
Well you need to be specific here. As far as I know, the key in a registry is a "folder" of properties. So did you mean get the value of a property? If so, try something like this:
(Get-ItemProperty HKLM:\Software\Microsoft\PowerShell\1\PowerShellEngine -Name PowerShellVersion).PowerShellVersion
First we get an object containing the property we need with Get-ItemProperty and then we get the value of for the property we need from that object. That will return the value of the property as a string. The example above gives you the PS version for "legacy"/compatibility-mdoe powershell (1.0 or 2.0).
Following code will enumerate all values for a certain Registry key, will sort them and will return value name : value pairs separated by colon (:):
$path = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NETFramework';
Get-Item -Path $path | Select-Object -ExpandProperty Property | Sort | % {
$command = [String]::Format('(Get-ItemProperty -Path "{0}" -Name "{1}")."{1}"', $path, $_);
$value = Invoke-Expression -Command $command;
$_ + ' : ' + $value; };
Like this:
DbgJITDebugLaunchSetting : 16
DbgManagedDebugger : "C:\Windows\system32\vsjitdebugger.exe" PID %d APPDOM %d EXTEXT "%s" EVTHDL %d
InstallRoot : C:\Windows\Microsoft.NET\Framework\
Not sure at what version this capability arrived, but you can use something like this to return all the properties of multiple child registry entries in an array:
$InstalledSoftware = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" | ForEach-Object {Get-ItemProperty "Registry::$_"}
Only adding this as Google brought me here for a relevant reason and I eventually came up with the above one-liner for dredging the registry.
If you create an object, you get a more readable output and also gain an object with properties you can access:
$path = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NETFramework'
$obj = New-Object -TypeName psobject
Get-Item -Path $path | Select-Object -ExpandProperty Property | Sort | % {
$command = [String]::Format('(Get-ItemProperty -Path "{0}" -Name "{1}")."{1}"', $path, $_)
$value = Invoke-Expression -Command $command
$obj | Add-Member -MemberType NoteProperty -Name $_ -Value $value}
Write-Output $obj | fl
Sample output:
InstallRoot : C:\Windows\Microsoft.NET\Framework\
And the object:
$obj.InstallRoot = C:\Windows\Microsoft.NET\Framework\
The truth of the matter is this is way more complicated than it needs to be. Here is a much better example, and much simpler:
$path = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NETFramework'
$objReg = Get-ItemProperty -Path $path | Select -Property *
$objReg is now a custom object where each registry entry is a property name. You can view the formatted list via:
write-output $objReg
InstallRoot : C:\Windows\Microsoft.NET\Framework\
DbgManagedDebugger : "C:\windows\system32\vsjitdebugger.exe"
And you have access to the object itself:
$objReg.InstallRoot
C:\Windows\Microsoft.NET\Framework\

How do I manipulate data out of a variable in powershell

I have a power-shell script with which I am trying to back up a constantly changing number of SQL databases. Fortunately all of these databases are listed in a registry key. I am leveraging this in a for-each loop. The issue that I am having is that after grabbing the registry value that I want, when I try to pass it into my function to back up the databases there seems to be information in the variable that I can get rid of. If I output the contents of the variable to the screen by just calling the variable ($variable) is shows just fine. But if I write-host the variable to the screen the extra "content" that shows up when calling the function also shows up.
Here is the part of the script that generates the contents of the variable.
foreach ($childitem in get-childitem "HKLM:\SOFTWARE\Wow6432Node\Lanovation\Prism Deploy\Server Channels")
{$DBName = get-itemproperty Registry::$childitem | select "Channel Database Name"
write-host $DBname}
Here is what write-host displays :
#{Channel Database Name=Prism_Deploy_Sample_268CBD61_AC9E_4853_83DE_E161C72458DE}
but what I need is only this part :
Prism_Deploy_Sample_268CBD61_AC9E_4853_83DE_E161C72458DE
I have tried looking online at how to do this, and what I've found mentions things similar to $variable.split and then specifying my delimiters. But when I try this I get an error saying "Method invocation failed because [System.Management.Automation.PSCustomObject] doesn't contain a method named 'split'."
I'm at a loss as to where to go from where I'm at currently.
select-object will return an object that has the named properties that you "select". To get just value of that property, just access it by name:
write-host $DBname."Channel Database Name"
Sounds like it's returning a hash table row object.
Try
write-host $DBName.value
or, failing that, do a
$DBName | Get-member
When in doubt, get-member gives you a nice idea of what you are dealing with.
You should be able to write
foreach ($childitem in get-childitem "HKLM:\SOFTWARE\Wow6432Node\Lanovation\Prism Deploy\Server Channels")
{$DBName = get-itemproperty Registry::$childitem | select "Channel Database Name"
write-host $DBname.Name}
to get what you are looking for