I'm trying to manage some non-standard error in my functions (like a wrong input text), and I want to track those errors by writing a sort of log in a variable. I'm trying to write also the line number, and this is my code
$someVar = "line $($MyInvocation.ScriptLineNumber): at least 2 arguments needed, function has been called with only $args.Count arguments"
Sometimes it returns the correct number and sometimes it doesn't. Is this the correct way? Is there another method?
EDIT: I found that this problem could be related to an unconventional way to execute scripts that I use in order to bypass a permission problem on a specific the machine. I'll post a more detailed example as soon as I can
Any reason why you're not using param in your functions and making them mandatory?
Function A()
{
Param
(
[Parameter(Mandatory=$true)][String]$Arg1,
[Parameter(Mandatory=$true)][String]$Arg2
)
Write-Host "$Arg1 $Arg2"
}
If you run this function without any arguments then it will throw an error asking for the mandatory arguments.
try wrapping in $(). For example:
$someVar = "line $($MyInvocation.ScriptLineNumber): at least 2 arguments needed, function has been called with only $($args.Count) arguments"
Related
There's a few commands I need to run repetitively in Powershell, with some variable arguments in content and length, but some arguments must always be there. I don't want to forget those arguments, so is there a way to create a function that does this?
I have tried using things like Invoke-Expression, but when I use brackets in a command, Powershell thinks it's a type and tells me my cast is not valid. If I escape the argument list and provide an argument in the form Key=Value, PowerShell parses it into a System.Object[] and the command fails. I figured it might be better not to ask about how to fix that, but how to solve my root problem.
You can consider this a somewhat duplicate of How to always append an ampersand for certain commands? (MacOS / bash) but for PowerShell.
Just write your own version of the command as a Function with mandatory parameters:
Function RunMyCommand {
Param(
[Parameter(Position=0,mandatory=$true)][string]$Argument1,
[Parameter(Position=1,mandatory=$true)][int]$Argument2,
[Parameter(Position=2,mandatory=$true)]$Argument3
)
# Amend this according to the syntax and string manipulation required for your command
ThisIsMyCommand $Argument1 $Argument2 $Argument3
}
RunMyCommand -Argument1 ThisString -Argument2 ThisNumber -Argument3 ThisAnything
So I am using the kind of buggy Sapien powershell studio to make a powershell driven GUI application, and I am attempting to perform an ADSI query.
$nameOfDeviceInput is a System.Windows.Forms.TextBox
On one form, I have the following function:
$buttonPerformAction_Click={
if (FindInAD($nameOfDeviceInput.Text).Count -gt 0)
{
$buttonPerformAction.BackColor = 'Red'
$buttonPerformAction.Text = "System already exists in AD with that name. Try another name"
return
}
.....
}
On the "main" form, I have the function FindInAD
function FindInAd($nameOfSystem)
{
Write-Host "seeking system" $nameOfSystem
([adsisearcher]"(CN=$nameOfSystem)").FindAll()
}
FindInAd() is failing because for whatever reason, $nameOfSystem is set to 1, and if I don't explicitly cast it as a string, it gets implicitly cast to Int32 (obviously)
I have tried the following:
Fully qualifying the textbox input by notating the form it belongs to ( $adObjectModifier )
$buttonPerformAction_Click={
if (FindInAD($adObjectModifier.$nameOfDeviceInput.Text).Count -gt 0)
{
$buttonPerformAction.BackColor = 'Red'
$buttonPerformAction.Text = "System already exists in AD with that name. Try another name"
return
}
.....
}
Explicitly casting the $nameOfSystem parameter as a type of [string]
function FindInAd([string]$nameOfSystem)
{
Write-Host "seeking system" $nameOfSystem
([adsisearcher]"(CN=$nameOfSystem)").FindAll()
}
Passing a raw string into FindInAD from the AdObjectModifier form.
....
if (FindInAD("Test").Count -gt 0)
....
There is nothing else on the output pipeline at the time, (at least not from me) in between the method invocation. It is EventHandler > Function Call with String parameter
Why are the strings I'm passing getting changed to a digit???
EDIT: I think my passed parameter is being automatically replaced with the resulting boolean somehow, but this doesn't make any sense to me....
Your have a syntax problem:
FindInAD($nameOfDeviceInput.Text).Count # WRONG
Note: Wrong in this context means: the syntax is formally valid, but doesn't do what you expect - see the bottom section.
It should be:
(FindInAD $nameOfDeviceInput.Text).Count
PowerShell commands - functions, cmdlets, scripts and external programs - are invoked like shell commands - foo arg1 arg2 - and not like C# methods - foo('arg1', 'arg2').
That is:
Do not put (...) around the list of arguments.
However, you do need (...) around the call as a whole if you want a command call to participate in an expression, as shown above with the access to property .Count - see this answer for more information.
Separate arguments with spaces, both from each other and from the command name - do not use ,
, between arguments functions differently: It constructs an array that is passed as a single argument - see below.
You may pass simple strings (ones that contain neither spaces nor PowerShell metacharacters such as ; or &) as barewords; that is, quoting them is optional; e.g., instead of foo 'bar', you can call foo bar - see this answer for how PowerShell parses unquoted command arguments.
Also, if a target function or script has explicitly declared parameters (which binary cmdlets invariably do), such as -bar and -baz, you can pass your values as named arguments, i.e. by prepending them with the target parameter name; doing so is good practice in scripts: foo -bar arg1 -baz arg2
By contrast, calling methods of objects uses the syntax familiar from regular programming languages such as C# ($obj.foo('arg1', 'arg2'))
This difference relates two PowerShell's two fundamental parsing modes, explained in detail in this answer:
Commands are parsed in argument mode - as in shells.
Method calls and operator-based expressions are parsed in expression mode - as in regular programming languages.
These modes are required in order to allow PowerShell serve double duty: as a shell on the one hand, and as a scripting (programming) language on the other.
PowerShell can help you avoid this syntax problem:
Note that the problem isn't that using method syntax to call a command is invalid syntax, but that it doesn't work as intended, which can be difficult to diagnose.
In short: When you call command foo as foo('foo', 'bar'), ('foo', 'bar')is a 2-element array, which is then passed to foo as a single argument.
To prevent the problem to begin with, you can set Set-StrictMode to -Version 2 or higher, which makes PowerShell report an error if you accidentally use method syntax when calling a command:
# Turn on the check for accidental method syntax.
# Note: This also turns on ADDITIONAL checks - see below.
Set-StrictMode -Version 2
# This call now produces an ERROR, because the proper syntax would be:
# foo 'a' 'b'
foo('a', 'b')
Caveats:
Set-StrictMode -Version 2 comprises additional strictness checks that you must then also conform to, notably:
You must not reference non-existent variables.
You must not reference non-existent properties; see GitHub issue #2798 for an associated pitfall in connection with PowerShell's unified handling of scalars and collections.
An error is reported only for pseudo method calls with multiple arguments (e.g.,
foo('bar', 'baz')), not with only one; e.g., foo('bar') is accepted, because the single-argument case generally still (accidentally) works.
The errors reported for strictness violations are statement-terminating errors: that is, they only terminate the statement at hand, but by default the script continues; to ensure that overall execution aborts - on any type of error - you'd have to set
$ErrorActionPreference = 'Stop' at the start of your code. See this answer for more information.
As for what you tried:
FindInAD($nameOfDeviceInput.Text).Count
is the same as:
FindInAD ($nameOfDeviceInput.Text).Count
That is, the result of expression ($nameOfDeviceInput.Text).Count is passed as an argument to function FindInAD.
I have little experience with PowerShell in particular.
I'm trying to refactor some very commonly re-used code into a single script that can be sourced where it's needed, instead of copying and pasting this same code into n different scripts.
The scenario I'm trying to get looks (I think) like this:
#common.ps1:
param(
# Sure'd be great if clients didn't need to know about these
$some_params_here
...
)
function Common-Func-Uses-Params {
...
}
⋮
# foo/bar/bat.ps1:
# sure would love not to have to redefine all the common params() here...
. common.ps1 <pass-the-arguments>
Common-Func-Uses-Params $specific_Foo/Bar/Bat_Data
As the pseudo-comments above indicate, I've only been able to do this so far by capturing the params in the calling script as well.
I want to be in a situation where I can update the common code (say with a -Debug or -DryRun or -Url or whatever parameter) and not have to worry about updating all of the client code to match.
Is this possible?
You're missing two key things:
args - which captures all of (and only) the unbound arguments to the script
splatting (#) - which is used to pass arrays or hashtables to a command rather than flattening them like you'd get with $
When you combine these, you can easily pass all arguments onto another script, like so:
# foo.ps1
. common.ps1 #args
With a sourced file like this:
#common.ps1
param ([string]$foo = "foo")
echo "`$foo is $foo"
You get these output:
> foo.ps1 returns $foo is foo
> foo.ps1 -Foo bar returns $foo is bar
Note that, if you're trying to use the PowerShell ISE it might take you a while to figure this out or debug any of it. When you're in the debugger, both $args nor $MyInvocation.UnboundArguments will do their best to hide that information from you. They'll appear to be completely empty.
You can print the args with >> echo "$(#args)", but that also provides the very weird side effect of telling the Debugger to continue. I think the splatting is adding an extra newline and that's ending up in the Command Window.
The best workaround I have for that is to add $theargs = $args at the top of your script and remember to use $theargs in the debugger.
Using fish shell, I'm writing very simple script that checks the command execution
#!/usr/bin/fish
command
if $status
echo "Oops error"
else
echo "Worked OK"
#...
end
And get the error message:
fish: Variables may not be used as commands. Instead, define a function like “function status; 0 $argv; end”. See the help section for the function command by typing “help function”.
The message looks pretty straight forward but no "defining function like..." nor "help function" helps solving the problem.
There is also a 'test' command, that sounds promising. But docs say it is to be used to check files...
How this simple thing should be done with fish shell?
Heh... And why all documentation is SO misleading?..
P.S. Please, don't write about 'and' command.
Fish's test command currently works exactly like POSIX test (i.e. the one you'll find in bash or similar shells). It has a couple of operations, including "-gt", "-eq", "-lt" to check if a number is bigger, equal or less than another number, respectively.
So if you want to use test, you'll do if test $status -eq 0 (a 0 traditionally denotes success). Otherwise, you can check the return value of a command by putting it in the if clause directly like if command (which will be true if the command returns 0) - that's what fish is trying to do here, which is why it complains about a variable being used in place of a command.
I'm using RRDs::Simple function and it needs bunch of parameters.
I have placed these parameters in a special variable (parsing, sorting and calculating data from a file) with all quotes, commas and other stuff.
Of course
RRDs::create ($variable);
doesn't work.
I've glanced through all perl special variables and have found nothing.
How to substitute name of variable for the data that contained in that variable?
At least could you tell me with what kind of tools(maybe another special variables) it can be done?
Assuming I'm understanding what you're asking:
You've build the 'create' data in $variable, and are now trying to use RRDs::create to actually do it?
First step is:
print $variable,"\n"; - to see what is actually there. You should be able to use this from the command line, with rrdtool create. (Which needs a filename, timestep, and some valid DS and RRA parameters)
usually, I'll use an array to pass into RRDs::create:
RRDs::create ( "test.rrd", "-s 300",
"DS:name:GAUGE:600:U:U", )
etc.
If $variable contains this information already, then that should be ok. The way to tell what went wrong is:
if ( RRDs::error ) { print RRDs::error,"\n"; }
It's possible that creating the file is the problem, or that your RRD definitions are invalid for some reason. rrdtool create on command line will tell you, as will RRDs::error;