Run a long Powershell command as parameter when executing a BATCH file [duplicate] - powershell

This question already has answers here:
Convert a small PS script into a long line in a .BATch file
(3 answers)
Closed 4 years ago.
I want to run the following Powershell command by executing a .bat file:
Add-Type -TypeDefinition #'
using System.Runtime.InteropServices;
[Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IAudioEndpointVolume
{
// f(), g(), ... are unused COM method slots. Define these if you care
int f(); int g(); int h(); int i();
int SetMasterVolumeLevelScalar(float fLevel, System.Guid pguidEventContext);
int j();
int GetMasterVolumeLevelScalar(out float pfLevel);
int k(); int l(); int m(); int n();
int SetMute([MarshalAs(UnmanagedType.Bool)] bool bMute, System.Guid pguidEventContext);
int GetMute(out bool pbMute);
}
[Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMMDevice
{
int Activate(ref System.Guid id, int clsCtx, int activationParams, out IAudioEndpointVolume aev);
}
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMMDeviceEnumerator
{
int f(); // Unused
int GetDefaultAudioEndpoint(int dataFlow, int role, out IMMDevice endpoint);
}
[ComImport, Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")] class MMDeviceEnumeratorComObject { }
public class Audio
{
static IAudioEndpointVolume Vol()
{
var enumerator = new MMDeviceEnumeratorComObject() as IMMDeviceEnumerator;
IMMDevice dev = null;
Marshal.ThrowExceptionForHR(enumerator.GetDefaultAudioEndpoint(/*eRender*/ 0, /*eMultimedia*/ 1, out dev));
IAudioEndpointVolume epv = null;
var epvid = typeof(IAudioEndpointVolume).GUID;
Marshal.ThrowExceptionForHR(dev.Activate(ref epvid, /*CLSCTX_ALL*/ 23, 0, out epv));
return epv;
}
public static float Volume
{
get { float v = -1; Marshal.ThrowExceptionForHR(Vol().GetMasterVolumeLevelScalar(out v)); return v; }
set { Marshal.ThrowExceptionForHR(Vol().SetMasterVolumeLevelScalar(value, System.Guid.Empty)); }
}
public static bool Mute
{
get { bool mute; Marshal.ThrowExceptionForHR(Vol().GetMute(out mute)); return mute; }
set { Marshal.ThrowExceptionForHR(Vol().SetMute(value, System.Guid.Empty)); }
}
}
'#
[audio]::Volume = 1
The problem with cmd command prompt is that it interprets a new line of code as execute this command.
However, when I enter everything into a PowerShell command line, it does not do so.
Is there any possibility to run this whole PowerShell script by executing a batch script?
I have already tried powershell -command "and the whole script", but that did not work either... cmd keeps thinking a new line means to execute it.

Try this if you want to execute your PS1 File:
powershell.exe -executionpolicy bypass -file "YOUR_FILE_NAME.ps1"
If you want to do everythin in one batch File do this:
powershell.exe
"Your Command"
You just need to put your command into the next line.

First of all, if you have a long PowerShell command, the maximum limit of characters per cmd command line can be easily reached (I believe it is ~8191 characters?).
Furthermore, it is quite uncommon to execute such big PowerShell commands directly in the cmd command line. Usually you should put it inside a file ending with .ps1, and then you execute it using the following command:
powershell.exe -executionpolicy bypass -file script.ps1
In case you really need to run the PowerShell command as you mentioned, you must first modify it a little bit. Take as example the following PS script:
function Say-Hello
{
[CmdletBinding()]
Param
(
[string] $name
)
Process
{
# Let's say hello!
$str = "Hello " + $name
Write-Output $str
}
}
Say-Hello "Jason"
The trick is to replace all \r\n line endings with \n, using a text editor (like Notepad++ for instance):
HOWEVER, you must first add some ; at the end of many of your PowerShell commands, because that is the only way you can tell PowerShell that a new PowerShell command is being issued. Otherwise, PowerShell may take 2 lines of your code and execute them as a single one, since they look all concatenated after you removed the newlines.
Then remove all line comments from your code, and escape all double quotes (or alternatively, just replace them with single quotes):
function Say-Hello
{
[CmdletBinding()]
Param
(
[string] $name
)
Process
{
$str = 'Hello ' + $name;
Write-Output $str
}
}
Say-Hello 'Jason'
Now you are ready to copy it from your text editor tool (Notepad++ in my case) and paste it to your command line like this:
powershell.exe -executionpolicy bypass -command "function Say-Hello { [CmdletBinding()] Param ([string] $name) Process { $str = 'Hello ' + $name; Write-Output $str }} Say-Hello 'Jason'"
And the expected output for that is:
Hello Jason

Related

Assign empty string to a variable and change the value while executing the powershell script

I have a sample powershell script say test.ps1 with the below contents
$Arg=" "
Function EnableArg()
{
$Arg= "arg"
}
Function DisableArg()
{
$Arg= " "
}
Function Print()
{
Write-Host "Value - $Arg"
}
If I run this script using powershell ISE and try the following:
Print - > Value -
EnableArg
Print -> Value -
When I executed step 2 and then step 3, I was expecting
Value - arg
But unfortunately it shows
Value -
Not sure if the script is being reset everytime. I also tried explicitly declaring the type to [string] but no luck. Thanks in advance!
Instead of crossing scope boundaries, I recommend you use functions in a more appropriate fashion. You pass in parameters/arguments and you return output. Meaning you will capture the output of a function in a variable and pass arguments into a function.
Function EnableArg()
{
"arg"
}
Function DisableArg()
{
" "
}
Function Print($InternalArg)
{
Write-Host "Value - $InternalArg"
}
$arg = EnableArg
print $arg
$arg = DisableArg
Print $arg

Issue while executing powershell script through System.Diagnostics.Process or System.Management.Automation.Runspaces

Need your help in solving this following issue.
We have a powershell script like:
invoke-command -ScriptBlock { [cmdletbinding()]
param(
[parameter(mandatory=$True)]
[string] $ticktfilepath
)
$ticketdetails=get-content $ticktfilepath |%{if ( $_ -like '"AB*' ) {$_}}|%{echo "$($_.Split(',')[7].Split('"')[1])=$($_.Split(',')[3].Split(':')[0].Split(' ')[$_.Split(',')[3].Split(':')[0].Split(' ').length-1])=$($_.Split(',')[0].Split('"')[1]);"}
write-output $ticketdetails } -ArgumentList 'D:\file.csv'
This script reads a csv file and for those lines in the csv with “AB…” at the start, does some string parsing. The csv file passed has rows with the “AB…” and hence result are returned. This runs perfectly when executing through powershell console or ISE.
But as per our requirement, where we trying to execute the same script through:
1. System.Diagnostics.Process with process start info having file name as powershell.exe and the argument at the above script. It fails for –like. i.e at |%{if ( $_ -like '"AB*' ). It is always false for the condition even though it is expected to be true.
N.B. other powershell script works perfectly with this approach
2. Exactly Similar issue when executed through System.Management.Automation.Runspaces
So looks like some constraint using the “–like” operator.
We even with the System.Diagnostics.Process approach tried writing the script line by line leveraging the Process.StandardInput.WriteLine(line) but then powershell hangs.
Any pointer to address this will be highly appreciated.
While using System.Diagnostics.Process, I used something like:
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.UseShellExecute = false;
processStartInfo.RedirectStandardError = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.FileName = "powershell.exe";
processStartInfo.Arguments = <ScriptContent>;
Process powerShellProc = new Process();
powerShellProc.StartInfo = processStartInfo;
powerShellProc.Start();
string successMessage = powerShellProc.StandardOutput.ReadToEndAsync().Result;
string errorMessage = powerShellProc.StandardError.ReadToEndAsync().Result;
powerShellProc.WaitForExit();
where,
ScriptContent- is the above powershell script.
Instead of like, even tried with startswith but then also the same result. But with powershell console or iSE, it works perfectly.
It's probably too late to answer :)
You shouldn't call powerShellProc.StandardOutput.ReadToEndAsync().Result before process actually finished. You can use Invoke-Executable function from How to capture process output asynchronously in powershell?
The following order is working
$outTask = $oProcess.StandardOutput.ReadToEndAsync();
$errTask = $oProcess.StandardError.ReadToEndAsync();
$bRet=$oProcess.WaitForExit($TimeoutMilliseconds)
$outText = $outTask.Result;
$errText = $errTask.Result;

Powershell arguments list passing like -a <args list> -d <args list>

I want to write a powershell ps1 script, which needs 2 argument sets,(-a -d) and each can have upto n attributes. How to implement that?
example : DoTheTask -a <task name 1> <task name 2> ... -d <machine name 1> <machine name 2>...
You can do this:
param(
[string[]]$a,
[string[]]$d
)
write-host $a
write-host ----
write-host $d
Then you can call DoTheTask -a task1,task2 -d machine1,machine2
Can you organize your task names and machines names in such a way that they can be put in to a single string with delimiters.
In other words, could your -a argument be a string a comma-separated task names and your -d argument be a string of comma-separated machine names? If so, then all you need to do is parse the string into its components at the start of your script.
If you are passing these arguments to the script itself, you could leverage the $args internal variable, though key/value mapping will be a little trickier since PowerShell will interpret each statement as an argument. I suggest (like others) that you use another separator so that you can do the mappings easier.
Nonetheless, if you want to continue doing it this way, you can use a function like the below:
Function Parse-Arguments {
$_args = $script:args # set this to something other than $script:args if you want to use this inside of the script.
$_ret = #{}
foreach ($_arg in $_args) {
if ($_arg.substring(0,1) -eq '-') {
$_key = $_arg; [void]$foreach.moveNext() # set the key (i.e. -a, -b) and moves to the next element in $args, or the tasks to do for that switch
while ($_arg.substring(0,1) -ne '-') { # goes through each task until it hits another switch
$_val = $_arg
switch($_key) {
'-a' {
write-host "doing stuff for $_key"
$ret.add($_key,$_val) # puts the arg entered and tasks to do for that arg.
}
# put more conditionals here
}
}
}
}
}

Parse powershell script parameters

Is there an easy way to parse the params from a powershell script file
param(
[string]$name,
[string]$template
)
I have started reading the file and wondered if there is a better way, maybe by a help/man command?
class PowerShellParameter {
public string Name;
public string Type;
public string Default;
}
string[] lines = File.ReadAllLines(path);
bool inparamblock = false;
for (int i = 0; i < lines.Length; i++) {
if (lines[i].Contains("param")) {
inparamblock = true;
} else if (inparamblock) {
new PowerShellParameter(...)
if (lines[i].Contains(")")) {
break;
}
}
}
There are at least two possibilies. First one (imho better): use Get-Command:
# my test file
#'
param(
$p1,
$p2
)
write-host $p1 $p2
'# | Set-content -path $env:temp\sotest.ps1
(Get-Command $env:temp\sotest.ps1).parameters.keys
For all members look at
Get-Command $env:temp\sotest.ps1 | gm
#or
Get-Command $env:temp\sotest.ps1 | fl *
The other (harder way) is to use regular expression
[regex]::Matches((Get-Help $env:temp\sotest.ps1), '(?<=\[\[-)[\w]+') | select -exp Value
I like the solution with Get-Command proposed by #stej. Unfortunately it does not work if script parameters have explicit types specified and an assembly of such a type is not yet loaded into the session. That is why I still use this script: Get names of script parameters
I'm not really sure what you're after, is it documenting your scripts? In that case have a look at Get-Help about_Comment_Based_Help. It will tell you how to do that, and after that you can use Get-Help on your script/module.
If you're after more strict parameter handling, take a look at about_functions_advanced_parameters and about_functions_cmdletbindings on how to better structure parameters. For example,
[Parameter(Position=0,Mandatory=$true,HelpMessage='Enter
architecture("OSX","WinXP","Win7","Linux")')]
[ValidateSet("OSX","WinXP","Win7","Linux")]
[string]$architecture
will make that parameter mandatory, read from position 0 of the command, allow only a value from the given set, and give a brief help message when asking for input if that parameter was not given.

How do I send a Carriage Return with Windows Netcat?

I want to type directly into the Powershell prompt too, not pipe in a text file.
Foo`r doesn't work for me. For example:
echo "RJ`r`n" | .\nc.exe -u 192.168.1.247 2639
but what I'd really like to do is just
.\nc.exe -u 192.168.1.247 2639
then start typing into the prompt.
Try:
"Foo`ndoes work for me"
If you need a full CRLF sequence then:
"Foo`r`ndoes work for me"
Note that the escapes chars only work in double-quoted strings - not single quoted strings.
Update: Redirect in < is not supported in PowerShell at this time so you can only get stdin to the exe from the interactive prompt using the pipeline. Is it possible nc.exe is expecting another character (or escape char) to terminate the input? I know that console exes can receive stdin from PowerShell. If you have the C# compiler, you can see this by compiling the following source (csc echostdin.cs):
using System;
public class App
{
public static void Main()
{
int ch;
while ((ch = Console.In.Read()) != -1)
{
Console.Write((char)ch);
}
}
}
Then execute the exe:
PS> "foo`r`n" | .\echostdin.exe
foo
PS>