I have to get the File directory the script is in as an variable and then split into following elements:
Example Path C:\Name\Season 1\script.ps1
Now what I would need is a variable that would be
var1 = "Name" and
var2 = "1" which needs to just take the Number from "Season 1"
Structure will always be the same --> C:\var1\Season var2\script.ps1 just different names
Thanks in adanvce for everyone willing to help me out as I have no clue on Powershell xd
You can get the full path to the currently executing script using the $PsScriptRoot automatic variable. You can then use the -match operator with Regular Expressions to get the parts you want:
$PsScriptRoot -match '\\(?<Var1>.*)\\Season (?<Var2>\d+)' | Out-Null
Assuming there is a match, then the values you want will be placed in the $Matches automatic variable. So, if your script is C:\MyFavShow\Season 4\Script.ps1, then the you can get the values after running the above by simply outputting Matches:
$Matches
Which would show:
Name Value
---- -----
Var2 4
Var1 MyFavShow
0 \Directory1\Season 4
Access the items individually like this: $Matches.Var1, which would output MyFavShow.
A couple of notes:
$PsScriptRoot isn't populated from the command-line, only when running a script file.
The same matching technique can be used against any path, not just the script root. So, if you enumerate some directories (using, say, Get-ChildItem), you can still match against them.
$Matches does not reset after every use, only if a match is made, so if you do enumerate a lot of paths and check each one, you probably want to also check for an actual match before using the variables (as they may still be set to whatever the previous match was):
if($directory -match '\\(?<Var1>.*)\\Season (?<Var2>\d+)') {
# Do something with the values
}
You can reset $Matches using: $Matches.Clear()
Is there a way to find a list of script files that reference a given module (.psm1)? In other words, get all files that, in the script code, use at least 1 of the cmdlets defined in the module.
Obviously because of PowerShell 3.0 and above, most of my script files don't have an explicit Import-Module MODULE_NAME in the code somewhere, so I can't use that text to search on.
I know I can use Get-ChildItem -Path '...' -Recurse | Select-String 'TextToSearchFor' to search for a particular string inside of files, but that's not the same as searching for any reference to any cmdlet of a module. I could do a search for every single cmdlet in my module, but I was wondering if there is a better way.
Clarification: I'm only looking inside of a controlled environment where I have all the scripts in one file location.
Depending on the scenario, the callstack could be interesting to play around with. In that case you need to modify the functions which you want to find out about to gather information about the callstack at runtime and log it somewhere. Over time you might have enough logs to make some good assumptions.
function yourfunction {
$stack = Get-PSCallStack
if ($stack.Count -gt 1) {
$stack[1] # log this to a file or whatever you need
}
}
This might not work at all in your scenario, but I thought I throw it in there as an option.
The Microsoft.PowerShell_profile.ps1 script I am using creates a lot of variables when it runs. I have set all the variables' scope to "Script", but the variables used in the script never go out-of-scope.
I would like the variables to go out-of-scope once the script is done running and control is handed over to me.
If I compare the number of global, local, and script variables I have, I come up with the same number.
Example:
# Profile script does what it does.
Get-Variable -Scope Global | Measure-Object
Get-Variable -Scope Local | Measure-Object
Get-Variable -Scope Script | Measure-Object
Output:
60
60
60
Currently, I am capturing a snapshot of the variables at the beginning of my profile script, then removing any new variables at the end.
Example:
$snapshotBefore = Get-Variable
$profileVar1 = 'some value'
$profileVar2 = 'some other value'
$snapshotAfter = Get-Variable
# Compare before and after, and create list of new variables.
Remove-Variable $variablesToRemove
Yes, PowerShell profiles are dot-sourced by design, because that's what allows the definitions contained in them (aliases, functions, ...) to be globally available by default - which is, after all, the main purpose of profile files.
Unfortunately, there is no scope modifier that allows you to create a temporary scope for variables you only want to exist while the profile is loading - even scope local is effectively global in a profile script; similarly, using scope private is also not an option, because the profile's script scope - due to being dot-sourced - is the global scope.
Generally speaking, you can use & (the call operator) with a script block to create variables inside that block that are scoped to that block, but that is usually at odds with creating globally available definitions in a profile, at least by default.
Similarly, calling another script without dot-sourcing it, as in your own answer, will not make its definitions globally available by default.
You can, however, create global elements from non-dot-sourced script blocks / script by specifying the global scope explicitly; e.g.: & { $global:foo = 'Going global' }, or & { function global:bar { 'global func' } }.
That said, the rationale behind dot-sourcing profiles is likely that it's easier to make all definitions global by default, making the definition of typical elements of a profile - aliases, functions, drive mappings, loading of modules - simpler (no need to specify an explicit scope).
By contrast, global variables are less typical, and to define the typical elements listed above you don't usually need script-level (and thus global) variables in your profile.
If you still need to create (conceptually) temporary variables in your profile (which is not a requirement for creating globally available aliases, functions, ...):
A simple workaround is to use an exotic variable name prefix such as __ inside the profile script to reduce the risk of their getting referenced by accident (e.g, $__profileVar1 = ...).
In other words: the variables still exist globally, but their exotic names will typically not cause problems.
However, your approach, even though it requires a little extra work, sounds like a robust workaround, here's what it looks like in full (using PSv3+ syntax):
# Save a snapshot of current variables.
# * If there are variables that you DO want to exist globally,
# define them ABOVE this command.
# * Also, load MODULE and dot-source OTHER SCRIPTS ABOVE this command,
# because they may create variables that *should* be available globally.
$varsBefore = (Get-Variable).Name
# ... define and use temporary variables
# Remove all variables that were created since the
# snapshot was taken, including $varsBefore.
Remove-Variable (Compare-Object $varsBefore (Get-Variable).Name).InputObject
Note that I'm relying on Compare-Object's default behavior of only reporting differences between objects and, assuming you haven't tried to remove any variables, only the variables added are reported.
Note that while it can be inferred from the actual behavior of profile files that they are indeed dot-sourced - given that dot-sourcing is the only way to add elements to the current scope (the global scope, in the case of profiles) -
this fact is not explicitly documented as such.
Here are snippets from various help topics (as of PSv5) that provide clues (emphasis mine):
From Get-Help about_Profiles:
A Windows PowerShell profile is a script that runs when Windows PowerShell
starts. You can use the profile as a logon script to customize the
environment. You can add commands, aliases, functions, variables, snap-ins,
modules, and Windows PowerShell drives. You can also add other
session-specific elements to your profile so they are available in every
session without having to import or re-create them.
From Get-Help about_Variables:
By default, variables are available only in the scope in which
they are created.
For example, a variable that you create in a function is
available only within the function. A variable that you
create in a script is available only within the script (unless
you dot-source the script, which adds it to the current scope).
From Get-Help about_Operators:
. Dot sourcing operator
Runs a script in the current scope so that any functions,
aliases, and variables that the script creates are added to the current
scope.
From Get-Help about_Scopes
But, you can add a script or function to the current scope by using dot
source notation. Then, when a script runs in the current scope, any
functions, aliases, and variables that the script creates are available
in the current scope.
To add a function to the current scope, type a dot (.) and a space before
the path and name of the function in the function call.
So it does sounds like Powershell dot-sources the profile. I couldn't find a resource that specifically says that, or other forums that have asked this question.
I have found an answer, and wanted to post it here.
I have changed my profile to only call a script file. The script now has its own scope, and as long as the variables aren't made global, they will go out-of-scope once the profile finishes loading.
So now my profile has one-line:
& (Split-Path $Path $profile -Parent | Join-Path "Microsoft.PowerShell_profile_v2.ps1")
Microsoft.PowerShell_profile_v2.ps1 can now contain proper scope:
$Global:myGlobalVar = "A variable that will be available during the current session"
$Script:myVar = "A variable that will disappear after script finishes."
$myVar2 = "Another variable that will disappear after script finishes."
What this allows, is for the profile script to import modules that contain global variables. These variables will continue to exist during the current session.
I would still be curious why Microsoft decided to call the profile in this way. If anyone knows, and would like to share. I would love to see the answer here.
I have two registry files (.reg) exported using powershell. I would like to compare the difference of the two files to the registry, ideally using powershell. I have been using compare-object but that compares the files at the text level. I want to "pre-load" the files into memory and compare them at the key/property level to determine which keys have changed. I would then want to create a third .reg file with the changes and apply this to the registry.
Is this possible, eg using the Compare-Object?
Multiple ideas, none of them what I would call good. I cannot find a better way, even using .NET APIs.
(Compare-Object): Use 'reg.exe' to export the target tree to a file, then Get-Content both files, and do a Compare-Object on their contents.
(Manual): Use get-content on new.reg, then parse each line with either split or regex-fu. For each item, get-itemproperty on the target and validate the values of the properties and child keys
(Compare-Object): Import new.reg into temporary registry location, then use Get-ChildItem on both trees and compare all the objects
I've written a powershell script that I run several times a day. It's getting to be somewhat of a chore to execute the script manually (from within Powergui or the shell), so I'd like to create a frontend which prompts me for the variables. I've found that Primalforms can supply me with pre-populated fields that can be adjusted if needed.
My problem is that I would like to create a gui and pass ALL the variables to my external script (this script is already written and will not be part of the Primalforms project).
How would I do this? Or should I pass the variables manually? How would I do that?
(I do not think this would be specific to Primalforms.. I'm rather executing a script with variables with another script as input.)
Any help would be greatly appreciated!
Use splatting. Collect all the values for the parameters in a hashtable (key names match parameter names) and assign each name the value of the parameter from the corresponding text feild in your form. Then pass the hashtable to script B. The following assumes that you have two text fields with names of: filter and path.
## scriptA ##
$params = #{
path=$path.text
filter=$filter.text
}
D:\Scripts\scriptB.ps1 #params