I would like to know how to call a powershell function using a .Bat file.
I have this simple function:
(Script.ps1)
function testfunction
{
Write-Log "Calling from Bat file"
}
and I would like to call the function testfunction within the .Bat File.
powershell .\Script.ps1 ...
I noticed that there is a switch -ExecutionPolicy ByPass which allows the batch file to run using Powershell. This works here, taken from the answer by vonPryz.
#echo off
:: Create a test script file
echo function test { write-host "Called from %~f0" } > s.ps1
powershell -ExecutionPolicy ByPass -command ". "%cd%\s.ps1"; test;"
del s.ps1
pause
Start Powershell with -command switch, include the script and call the function. You need to dot source the script before its function can be called.
:: Create a test script file
C:\temp>echo function test { write-host "Called from .bat" } > c:\temp\s.ps1
C:\temp>powershell -command ". c:\temp\s.ps1; test;"
:: Output
Called from .bat
Also, take a look at some samples.
I know this is very old but I was recently trying to do this same thing and it took me a long time to find out what to do. The answer is amazingly simple
In BatchFile (RunMe)
powershell -command ". Script.ps1; Set-SomeFunction %1 %2 %3 %4 %5"
Then you can call the batch like
RunMe -SomeParm val -SomeParm2 val -SomeSwitch -SomeBool $true
also, to run as admin
powershell -command "Start-Process -verb runas powershell" "'-noexit -command ". Script.ps1; Set-SomeFunction %1 %2 %3 %4 %5 %6 %7 %8 %9'"
I usually do it like that:
powershell {Set-ExecutionPolicy unrestricted}
powershell.exe -executionpolicy ByPass ./script/powerhsell-script.ps1 %par1% %par2%
powershell {Set-ExecutionPolicy Restricted}
Hope you need that :)
I've done it using
powershell.exe -ExecutionPolicy Bypass -file ".\Write_date.ps1"
Seems similar to what others have written but I'm not sure what vonPryz meant by needing the dot source the script before calling the script. The above worked on a Win7 with executionpolicy set to restricted.
Related
I am having difficulty with executing a command line in cmd from Powershell. The problem is I can't seem to use my global variables in the command line when cmd is opened from Poweshell. Below is the code I am working with but to no avail. Can someone please provide guidance on this issue?
Start-Process -FilePath "C:\Windows\System32\cmd.exe" -verb RunAs -ArgumentList {/k set Name="$cmdname" set Item="$cmditem" setlocal EnableDelayedExpansion echo "%Name%" }
Thanks,
Roger
The syntax is awkward for sure:
Start-Process -FilePath "C:\Windows\System32\cmd.exe" -ArgumentList "/k set Name=$cmdname & set Item=$cmditem & call echo %name%"
Some of the reasoning here is:
cmd: additional commands need to be separated by &
cmd: the set command takes everything after = until special characters like &
cmd: setlocal EnableDelayedExpansion doesn't really apply here. Use call to delay instead.
delayed variable syntax is also !var! rather than %var%
Powershell: using brackets in -ArgumentList {stuff} sends as a literal string, and variables aren't expanded
My Windows Batch shall be started by the user without administrator privileges. At some step, it shall call itself with elevated privileges. I have learned that this is possible using the PowerShell's runas feature (batch.bat ⭢ PowerShell ⭢ batch.bat). This works like a charm.
Unfortunately, I am not able to receive the exit code from the elevated batch execution. I always get 1, although there is not any error message. I have no idea at which return the exit code gets lost, 1st (batch back to PowerShell) or 2nd (PowerShell back to batch).
I believe, I have tried all of the plenty suggested answers from similar questions, but apparently I am unable to get it going. I need advice.
MVE which should indicate that the elevated batch returns 0:
#echo off
echo param=%~1
openfiles /local >nul 2>&1
IF %ERRORLEVEL% EQU 0 (
echo elevated, exit 0
pause
exit 0
) ELSE (
echo not elevated. trying to elevate.
powershell start-process -wait -verb runas '%0' -argumentlist /foo
echo powershell returned %errorlevel%.
)
Nota bene (edited to eliminate misunderstanding): while the non-elevated call (by the user) does not require any parameter, the elevated call introduces an additional parameter '/foo'. This makes things worse for me because I did not find a solution to not lose this parameter. However, this appears to be a rather unusual use case.
To solve the argument problem, you could use
powershell start-process -wait -verb runas '%0' -argumentlist '/additional-arg %*'
The exit code problem:
The first problem is the line
echo powershell returned %errorlevel%.
This can't work, because it's inside a code block and %errorlevel% will be expanded even before powershell will be called and therefore it is always 1 - the result of openfiles /local ...
But even with delayed expansion, I got always 0, probably because it's the exitcode of the successful runas, that it was able to start your batch.
You could use a work around and store the exitcode in a temporary file
#echo off
setlocal EnableDelayedExpansion
echo param=%*
openfiles /local >nul 2>&1
IF %ERRORLEVEL% EQU 0 (
echo elevated, exit 13
pause
echo 13 > "%temp%\_exitcode.tmp"
rem *** using 14 here to show that it doesn't be stored in errorlevel
exit 14
) ELSE (
echo not elevated. trying to elevate.
powershell start-process -wait -verb runas '%0' -argumentlist '/additional-arg %*'
set /p _exitcode= < "%temp%\_exitcode.tmp"
del "%temp%\_exitcode.tmp"
echo powershell returned !_exitcode!, lvl !errorlevel!.
)
You aren't putting the PowerShell commands to execute in quotes, and you would do well to use the full path as well as include any arguments to the script. A generic way to invoke this, so it could be copied across scripts, your PowerShell invocation should look like so:
powershell -c "if([bool]'%*'){ Start-Process -Wait -Verb runas '%~dpnx0' -ArgumentList ('%*' -split '\s+') } else { Start-Process -Wait -Verb runas '%~dpnx0' }"
For your needs above, this could be simplified since you know you have arguments passed into the batch file to process:
powershell -c "Start-Process -Wait -Verb runas '%~dpnx0' -ArgumentList '/foo'
%~dpnx0 - Automatic batch variable, this is the full path to the current script, including the script name
%* - Automatic batch variable, this is all arguments passed into the script.
('%*' -split '\s'): This is a PowerShell expression takes the space-delimited %* variable and splits it on continuous whitespace, returning an array. For simplicity this does have a shortcoming in that it will split on spaces in between double quotes, but the regex can be tuned to account for that if needed.
This answer is worth a read for other automatic batch variables you may find use for in the future.
I have a problem to use a powershell script plugin, with my nagios agent (NRPE_NT.exe).
To solve this problem I've create underlying wrap batch file:
#ECHO OFF
SET SCRIPTPATH=%~d0%~p0check_process_mem.ps1
SET ARGS=%*
IF [%ARGS%] NEQ [] GOTO ESCAPE_ARGS
:POWERSHELL
PowerShell.exe -NoProfile -NonInteractive -NoLogo -ExecutionPolicy Unrestricted -Command "& { $ErrorActionPreference = 'Stop'; & '%SCRIPTPATH%' #args; EXIT $LASTEXITCODE }" %ARGS%
EXIT /B %ERRORLEVEL%
:ESCAPE_ARGS
SET ARGS=%ARGS:"=\"%
SET ARGS=%ARGS:`=``%
SET ARGS=%ARGS:'=`'%
SET ARGS=%ARGS:$=`$%
SET ARGS=%ARGS:{=`{%
SET ARGS=%ARGS:}=`}%
SET ARGS=%ARGS:(=`(%
SET ARGS=%ARGS:)=`)%
SET ARGS=%ARGS:,=`,%
SET ARGS=%ARGS:^%=%
GOTO POWERSHELL
Now, if I run it as dialog user, I haven't any problem, but if I run it with SYSTEM user, powershell session doesn't close, and I have to kill it, from tasklist.
What can I do, for solve this problem?
I can't update NRPE_NT.exe agent to NSClient+ (powershell compliance).
i've solve this problem with powershell plugin, without wrap batch file. I've declaire nrpe.cfg in this way:
command[check_X]=cmd /c echo C:\nrpe\libexec\check_process_mem.ps1 $ARG1$ -VM_w "$ARG2$" -VM_c "$ARG3$" ; exit $($LastExitCode) | powershell.exe -NoProfile -NonInteractive -NoLogo -ExecutionPolicy Unrestricted -command -
and now it works correctly. Exit code and output of script is correct.
I am trying to call Powershell with the Exec method of the WshShell object. I am writing the script in JScript, but I have reproduced the problem in VBScript as well. Both of the following short test scripts will cause WSH to hang indefinitely:
test.js
var shell = new ActiveXObject("WScript.Shell");
WScript.Echo(shell.exec("powershell -Command $Host.Version; Exit").StdOut.ReadAll());
test.vbs
dim shell
set shell = CreateObject("WScript.Shell")
WScript.Echo shell.exec("powershell -Command $Host.Version; Exit").StdOut.ReadAll
Am I doing something wrong, or am I running into or a limitation/incompatibility? The Run method works very well, but I need to capture output, which it's not capable of doing.
Edit: I forgot to mention that my platform is Windows 7 Pro, 64-bit with PowerShell 3. I've tested on Windows XP with PowerShell 1 as well.
Edit 2: I've updated the test scripts that I'm running to fit with x0n's answer. Unfortunately, I'm still having trouble. Here are my current tests:
test.js:
var shell = new ActiveXObject("WScript.Shell");
WScript.Echo(shell.exec('powershell -noninteractive -noprofile -Command "& { echo Hello_World ; Exit }"').StdOut.ReadAll());
test.vbs:
dim shell
set shell = CreateObject("WScript.Shell")
WScript.Echo shell.exec("powershell -noninteractive -noprofile -Command ""& { echo Hello_World ; Exit }""").StdOut.ReadAll
You have to close StdIn:
var shell = new ActiveXObject("WScript.Shell");
var exec = shell.Exec('powershell -noninteractive -noprofile -Command "& { echo Hello_World ; Exit }"');
exec.StdIn.Close();
WScript.Echo(exec.StdOut.ReadAll());
Microsoft said:
StdIn is still open so PowerShell is waiting for input. (This is an
"implementation consideration" that we're hoping to fix in V2. The
PowerShell executable gathers all input before processing.) So
objexec.StdIn.Close() needs to be added.
Use:
powershell.exe -noninteractive -noprofile -command $host.version
as your string. For more complicated groups of commands, use this syntax:
powershell.exe -noninteractive -noprofile -command "& { $host.version; $host.version }"
I have a function (function1) which requires sta mode. I'd like tocall this function from a non sta mode poshconsole. This works when i have function1 in my Profile
$command = "function1 'MyArguments'"
powershell -sta -command $command
But how can i do this when i have the function1 not in the profile and i call
powershell -sta -noprofile -command $command
Can i execute multiple commands with a powershell.exe call? Or can i handover a customprofilepath?
You can separate multiple commands with a semicolon (;) for example, dot source your script containing your function, then call the function:
powershell -sta -noprofile -command ". c:\functions.ps1 ; function1 'MyArguments'"
There are some ways how to deal with STA:
Single Threaded Apartment in PowerShell V1
Asynchronicity in PowerShell
Thread.ApartmentState and PowerShell Execution Thread
In case you will call powershell.exe (because it is obviously easier), you can first load your script with the function and then execute function1 'My arguments'
powershell -sta -noprofile -command '<pathToYourScript>.ps1; function1 args'
I tried to use -file parameter and -command argument together, but that doesn't work.
If you are running the PowerShell Community Extensions you can use Invoke-Apartment e.g.:
PS> Invoke-Apartment -Apartment STA `
-Expr {[Threading.Thread]::CurrentThread.ApartmentState}
STA