Emulating 'source' command in powershell - powershell

I am trying to emulate bash's source command in powershell. The intent is to make any change to my microsoft.powershell_profile.psl and source it into the existing instance of powershell.
The following command works in command-line
$profile_content = [string]::join([environment]::newline,(get-content $profile))
invoke-expression $profile_content
All is good; I put the same into microsoft.powershell_profile.psl and it does not work.
function source{
$profile_content = [string]::join([environment]::newline,(get-content $args[0]))
invoke-expression $profile_content
}
Am I overlooking something?

What you want is already built into PowerShell:
. C:\path\to\some.ps1
See 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.
. c:\scripts.sample.ps1

Try changing your Microsoft.PowerShell_profile.ps1 to look like this:
function source{
$profile_content = [string]::join([environment]::newline,(get-content $args[0]))
invoke-expression $profile_content
}
source $profile
Basically, you can't just define the function, you need to call it in the file as well. HOWEVER, the way your function is set up, it will result in an infinite loop. Replace your function source() with something else.

Following should be enough:
function source { . $args }

Related

Why can't I see the output of another executable in an invoked ScriptBlock?

I am creating and invoking PowerShell ScriptBlock objects. I noticed that when the ScriptBlock includes another executable, I am not able to see the output from that process. Keyboard input is still accepted though.
Here is a simplified version of the problem. In this case, I do not see any output from cmd.exe but I can type 'exit{ENTER}' and return to PowerShell
function Test-CMD {cmd.exe}
$sb = [System.Management.Automation.ScriptBlock]::Create('Test-CMD')
$sb.Invoke()
Is there a way to get the executable output to the console? Other ScriptBlock output works as expected.
PowerShell is blocked until the method ($sb.Invoke()) returns - to avoid this, use the & call operator instead:
function Test-CMD {cmd.exe}
$sb = [System.Management.Automation.ScriptBlock]::Create('Test-CMD')
& $sb

Powershell Profile to append parameters to a certain command

I have a certain command that I want to be able to append a parameter to as a powershell profile function. Though I'm not quite sure the best way to be able to capture each time this command is run, any insight would be helpful.
Command: terraform plan
Each time a plan is run I want to be able to check the parameters and see if -lock=true is passed in and if not then append -lock=false to it. Is there a suitable way to capture when this command is run, without just creating a whole new function that builds that command? So far the only way I've seen to capture commands is with Start-Transcript but that doesn't quite get me to where I need.
The simplest approach is to create a wrapper function that analyzes its arguments and adds -lock=false as needed before calling the terraform utility.
function terraform {
$passThruArgs = $args
if (-not ($passThruArgs -match '^-lock=')) { $passThruArgs += '-lock=false'}
& (Get-Command -Type Application terraform) $passThruArgs
}
The above uses the same name as the utility, effectively shadowing the latter, as is your intent.
However, I would caution against using the same name for the wrapper function, as it can make it hard to understand what's going on.
Also, if defined globally via $PROFILE or interactively, any unsuspecting code run in the same session will call the wrapper function, unless an explicit path or the shown Get-Command technique is used.
Not to take away from the other answer posted, but to offer an alternative solution here's my take:
$Global:CMDLETCounter = 0
$ExecutionContext.InvokeCommand.PreCommandLookupAction = {
Param($CommandName, $CommandLookupEvents)
if ($CommandName -eq 'terraform' -and $Global:CMDLETCounter -eq 0)
{
$Global:CMDLETCounter++
$CommandLookupEvents.CommandScriptBlock = {
if ($Global:CMDLETCounter -eq 1)
{
if (-not ($args -match ($newArg = '-lock=')))
{
$args += "${newArg}true"
}
}
& "terraform" #args
$Global:CMDLETCounter--
}
}
}
You can make use of the $ExecutionContext automatic variable to tap into PowerShells parser and insert your own logic for a specific expression. In your case, youd be using terraform which the command input will be parsed for each token and checked against -lock= in the existing arguments. If not found, append -lock=true to the current arguments and execute the command again.
The counter you see ($Global:CMDLETCounter) is to prevent an endless loop as it would just recursively call itself without there being something to halt it.

Shorter execution of powershell script

Say I have a script to be executed in a single call, how do I do it?
Like, say I have a powershell script saved at E:\Fldr\scrpt.ps1.
Now if I have to normally execute that script is PowerShell ISE then I would have to use:
& "E:\Fldr\scrpt.ps1"
and the scrpt.ps1 gets executed.
But whatI want is, when I write a word, say "exeScrpt" instead of & "E:\Fldr\scrpt.ps1" then I want scrpt.ps1 to get executed.
Is there a way to do this?
Thank you for checking in..
You can wrap your call to the script in a function:
function Invoke-Script
{
E:\Fldr\scrpt.ps1
}
Then you can run your script by executing a single command anywhere after the definition:
Invoke-Script
Note that it is good practice to name your functions according to the Verb-Noun cmdlet naming standard, so something like Invoke-Script instead of exeScrpt. If you really want a single word as the name, then you can additionally create an alias for your function:
New-Alias -Name exeScrpt -Value Invoke-Script

How to load custom powershell profile with single command

I have a shared computer for which I want to create a custom PowerShell profile that I can load with a simple command but which otherwise never gets sourced.
I tried adding a function like this to the main $profile:
function custom() {. C:\Users\[username]\Documents\WindowsPowerShell\custom_profile.ps1}
The idea was that I could just type the command custom and it would load my custom profile.
This doesn't work, because it does the sourcing inside the function scope, and all of the functions and aliases are lost when I leave that scope.
I could just execute the entire . C:\Users\[username]\Documents\WindowsPowerShell\custom_profile.ps1 command, but I am looking for a way to do this with a single command.
If I were in bash, I'd just use something like alias custom=". C:\Users\[username]\Documents\WindowsPowerShell\custom_profile.ps1", but Powershell's alias doesn't work that way.
How can I do this in PowerShell?
Or, change the file to a psm1 (a powershell module) and then :
Function custom {
if(-not Get-Module custom_profile){
Import-Module 'C:\Users\[username]\Documents\WindowsPowerShell\custom_profile.psm1'
} else {
Remove-Module custom_profile
Import-Module 'C:\Users\[username]\Documents\WindowsPowerShell\custom_profile.psm1'
}
}
and then running
custom
will do what you want.
As noted in the comments you might need
Export-ModuleMember -Variable * -Function * -Alias *
if your module is supposed to export variables and aliases as well as functions.
A simple way might be to just use a variable instead of a function.
In profile:
$custom = 'C:\Users\[username]\Documents\WindowsPowerShell\custom_profile.ps1'
Interactively:
. $custom
Probably want to use a better name that's less likely to be overwritten, but same concept.
The most simple way would be to use PSReadLine, define your own handler to be executed, such as F5 to load custom prifile.
This is shorter then writing a command also as you have to press 1 key.
You should use PSReadLine anyway as it overpowers stock console in every aspect.

Powershell - getting the scriptname of calling script in dot-sourced function

is there any way to get the scriptname of the calling script into a dot-sourced ps1 script?
This would be awsome for logging.
eg: script test1.ps1 is calling a function from dot-sourced log.ps1.
The String "test.ps1" is needed in log.ps1. Is this possible?
Thanks in advance
If you are dot sourcing log.ps1 then the execution is still occurring within Test.ps1.
To get the name of the executing script use:
$ExecutingScript = $MyInvocation.MyCommand.Name
# Test.ps1
You can then use $ExecutingScript in whatever logging functions provided by the dot sourced script.
If you need the entire path to the executing script, you would use:
$MyInvocation.InvocationName
# C:\Whereever\Test.ps1
You can try :
$a = Split-Path $PSCommandPath -Leaf
I was trying to solve similar or maybe same problem - how to generate log file name based on script name. Solution from #SomeShinyObject is nice but when you have your logging script sourced from file, that is sourced from another file, then you have problem. I have arrived at following solution. In case anyone is interested.
$scriptFileName = Get-Item (Get-PSCallStack)[(Get-PSCallStack).length-1].ScriptName
$log = "$($scriptFileName.DirectoryName)\log\$($scriptFileName.Basename).log"
It basically gets highest frame from the call stack by first getting stack depth using (Get-PSCallStack).length then it uses this value in obtaining the frame itself and gets ScriptName from there, which is uppermost script in calling hierarchy.