Automating 'yes' prompts running ps1 script in NonInteractive PowerShell [duplicate] - windows

I have a PowerShell script (which I cannot change) with the following function inside it:
function Foo ([string] param1) {
[...]
$var1 = Read-Host "Test"
$var2 = Read-Host "Test2"
[...]
}
I want to call the function from my PowerShell script and want to prevent that the user has to input any values, instead I want to prepare hardcoded values.
I tried the following:
#("Var1Value", "Var2Value") | Foo "Param1Value"
But it still prompts the user. Any ideas?

During command discovery, functions take precedence over binary cmdlets, so you can "hide" Read-Host behind a fake Read-Host function:
# define local Read-Host function
function Read-Host {
param([string]$Prompt)
return #{Test = 'Var1Value'; Test2 = 'Var2Value'}[$Prompt]
}
# call foo
foo
# remove fake `Read-Host` function again
Remove-Item function:\Read-Host -Force

Related

Using variables outside a function

I am currently writing my first script in Powershell and I am already facing the first problem.
I would like to read the value from a variable in a function so that I can use this variable in another cmd-let later. The problem now is that the variable is only recognized inside the function block and not outside.
How do I get this to work?
Thanks for the help :-)
function Write-Log([string]$logtext, [int]$level=0)
{
if($level -eq 0)
{
$logtext = "[INFO] " + $logtext
$text = "["+$logdate+"] - " + $logtext
Write-Host $text
}
}
Send-MailMessage -To "<xxx#xxx.de>" -Subject "$text" -Body "The GPO backup creation was completed with the following status: `n $text" -SmtpServer "xxx#xxx.de" -From "xxx#xxx.de"
I would like to submit $text
This has to do with variable scoping behavior in PowerShell.
By default, all variables in the caller's scope is visible inside the function. So we can do:
function Print-X
{
Write-Host $X
}
$X = 123
Print-X # prints 123
$X = 456
Print-X # prints 456
So far, so good. But when we start writing to variables outside the function itself, PowerShell transparently creates a new variable inside the function's own scope:
function Print-X2
{
Write-Host $X # will resolve the value of `$X` from outside the function
$X = 999 # This creates a new `$X`, different from the one outside
Write-Host $X # will resolve the value of the new `$X` that new exists inside the function
}
$X = 123
Print-X2 # Prints 123, and 999
Write-Host $X # But the value of `$X` outside is still 123, unchanged
So, what to do? You could use a scope modifier to write to the variable outside the function, but the real solution here is to return the value from the function instead:
function Write-Log([string]$logtext, [int]$level=0, [switch]$PassThru = $true)
{
if($level -eq 0)
{
$logtext = "[INFO] " + $logtext
$text = "["+$logdate+"] - " + $logtext
Write-Host $text
if($PassThru){
return $text
}
}
}
$logLine = Write-Log "Some log message" -PassThru
Send-MailMessage -Subject $logLine ...
if you need to access a variable outside a function in Powershell you might need to use the global variable.
$global:myglobalvariable="This is a PowerShell global variable"
or if its a null
$global:myglobalvariable2 = $null

How to push a command to the function parameters from the command line arguments?

I want to write a function that runs a given command and outputs a given message if there are errors when running this command. Command and message are passed to the function as an arguments.
Actually, this small script is for the project "A Continuous Integration System" from the series "50 Lines or Less". Here in a project description, all shell scripts are written in bash but i have Windows and i want rewrite all shell scripts on powershell, for some practice.
This is code that i have now:
function rof{
param(
[string] $msg,
[scriptblock] $cmd
)
try {
Invoke-Command $cmd
}
catch {
$msg
}
}
I expected that when i run this function like
rof -msg "some error :(" -cmd {git log}
that command in {} will be invoked or "some error" will outputs if, for example there are no git repository.
But function outputs
git log
What am i doing wrong? I've never written in powershell before. Help me please. Thank you!
Treat PowerShell as just another shell, you don't need to use Invoke-Command to call a command. Just call it as is, since you have to pass the arguments of the command, using Invoke-Expression will be the best option here.
function rof{
param(
[string] $msg,
[string] $cmd
)
try {
Invoke-Expression $cmd
}
catch {
$msg
}
}
The scriptblock was successfully invoked from PowerShell's point of view, meaning there is no terminating error which triggers the catch. What you can do instead is checking the $LASTEXITCODE variable, which will contain the exit code from your external command.
function rof {
param(
[string] $msg,
[scriptblock] $cmd
)
& $cmd
if ($LASTEXITCODE -ne 0) {
$msg
$LASTEXITCODE
}
}
Example:
rof -msg "test" -cmd {cmd.exe /c exit 123}
test
123
However, let me add that this proxy function sounds like a bad idea. I don't know your exact use-case, but this will just invoke anything (very unsecure in a constrained PowerShell) and always returns a message that is defined by the caller. But how would the caller know, what the error might be...?

How call invoke-expression to pass arguments when calling a remote script in powershell? [duplicate]

This question already has answers here:
What is shortest possible way to download script from HTTP and run it with parameters using Powershell?
(2 answers)
Closed 1 year ago.
My script content:
param (
[string]$arg1 = "2.2.2",
[string]$arg2 = "master"
)
& {
Write-Host "show $arg1 and $arg2 .."
}
Then I want to call this script in remote machine via http.
Invoke-Expression (Invoke-Webrequest "https://x.x.x.x/myscript.ps1" -UseBasicParsing).Content
But I don't know how to pass parameters to the script. Like this?
Invoke-Expression (Invoke-Webrequest "https://x.x.x.x/myscript.ps1" -UseBasicParsing).Content -arg1 2.2.1 -arg2 dev
How can help me? thanks!
If I do not pass arguments, the following commands are working fine.
Invoke-Expression (Invoke-Webrequest "https://x.x.x.x/myscript.ps1" -UseBasicParsing).Content
Download the code, create a scriptblock from it, and provide the arguments when calling Invoke() on the scriptblock.
$script = Invoke-RestMethod -Uri 'https://gist.githubusercontent.com/gittorta/75838602b2712d49c44e85ea0bc179e9/raw/8392f437943f66d47731423b674b81bd74378e4b/myscript.ps1'
$sb = [scriptblock]::Create($script)
$arg1 = "test1"
$arg2 = "test2"
$sb.Invoke($arg1, $arg2)
Output
show test1 and test2 ..
Same command condensed into a one-liner
([scriptblock]::Create((Invoke-RestMethod -Uri 'https://gist.githubusercontent.com/gittorta/75838602b2712d49c44e85ea0bc179e9/raw/8392f437943f66d47731423b674b81bd74378e4b/myscript.ps1'))).Invoke("test1", "test2")

How to remove partial path from Get-Location output?

I'm trying to write a custom prompt for PowerShell and I was wondering how I would filter out the 1...n directories in the output of Get-Location.
function prompt {
"PS " + $(get-location) + "> "
}
So, if the path is too long I would like to omit some of the directories and just display PS...blah\blah> or something. I tried (get-container) - 1 but it doesn't work.
Use Split-Path with the -Leaf parameter if you want just the last element of a path:
function prompt {
"PS {0}> " -f (Split-Path -Leaf (Get-Location))
}
I wanted to make a more dynamic function. I do just basic string manipulation. You could do some logic nesting Split-Path but the string manipulation approach is just so much more terse. Since what you want to be returned wont be a fully validated path I feel better offering this solution.
Function Get-PartialPath($path, $depth){
If(Test-Path $path){
"PS {0}>" -f (($path -split "\\")[-$depth..-1] -join "\")
} else {
Write-Warning "$path is not a valid path"
}
}
Sample Function call
Get-PartialPath C:\temp\folder1\sfg 2
PS folder1\sfg>
So you can use this simple function. Pass is a string for the path. Assuming it is valid then it will carve up the path into as many trailing chunks as you want. We use -join to rebuild it. If you give a $depth number that is too high the whole path will be returned. So if you only wanted to have 3 folders being shown setting the $depth for 3.
Ansgar Wiechers' answer will give you the last directory but if you want a way to do multiple directories at the end of the filepath (using the triple dot notation) you can cast the directory path to a uri and then just get and join the segments:
function prompt {
$curPath = pwd
$pathUri = ([uri] $curPath.ToString())
if ($pathUri.Segments.Count -le 3) {
"PS {0}>" -f $curPath
} else {
"PS...{0}\{1}>" -f $pathUri.Segments[-2..-1].trim("/") -join ""
}
}
Or using just a string (no uri cast)
function prompt {
$curPath = pwd
$pathString = $curPath.Tostring().split('\') #Changed; no reason for escaping
if ($pathString.Count -le 3) {
"PS {0}>" -f $curPath
} else {
"PS...{0}\{1}>" -f $pathString[-2..-1] -join ""
}
}
$a = prompt
Write-Host $a
Then just change -2 to whatever you want to be the first directory and -le 3 to match. I typically use the uri cast when I have to run stuff through a browser or over connections to Linux machines (as it uses "/" as a path separator) but there is no reason to not use the string method for normal operations.

Call a script in SharePoint PowerShell post-deployment command line with array in parameters

I try to call on post-deployment action a powerShell script with an array parameter, but without success.
Here is the script:
param(
[string[]]$ModelNameList = "DefaultModelName"
)
Write-Host "Number of Models is: $($ModelNameList.Count)"
and this is the post-deployment command line:
%windir%\sysnative\windowspowershell\v1.0\powershell -File "$(ProjectDir)Scripts\Post_Deployment\Script.Post-Deployment.ps1" -ModelNameList "m2","m1"
Result : Number of Models is: 1
Running the same script in SharePoint2010 Management Shell return the correct result.
Result : Number of Models is: 2
CMD doesn't know anything about PowerShell arrays, so it passes "m2","m1" into the powershell script as a single string. You can see that when you add
Write-Host $ModelNameList[0]
Write-Host $ModelNameList[0].GetType()
to your PowerShell script. I think you have to split the argument inside your script:
if ($ModelNameList[0] -match ',') {
$ModelNameList = $ModelNameList[0].Split(',')
}

Resources