I need to create a function.
First variable must be an array with an unknown number of parameters plus another variable.
My problem is that I don't know how distinguish between them.
I post an example:
function conc-str {
param([string[]]$array,[string]$name)
foreach($item in $array) {
$str+= $item
}
write-host $str
}
conc-str(#("aa","bb","cc"),"dd")
The result of this function is
aa bb ccdd
but as you can see I loop array elements and concatenate them. I thought to get just
aa bb cc
Where is my mistake?
The way you call it is:
conc-str #("aa","bb","cc") "dd"
You don't use "," as a parameter seperator in PowerShell. It is just a space. The moment you put a "," it becomes a single parameter.
Related
I am inside function #1 and triggering function #2. Function #2 has a variable that I want to get back into function #1 and use it.
My output ends up being:
hey there
var is:
What I want is the output to be:
var is: hey there
Why is it that I can feed a function a variable, and it uses it, but when I change that variable in the #2 function, it does not change the variable after it returns?
$var = $null
function one() {
two($var)
write-host "var is:" $var
}
function two($var){
$var = "hey there"
return($var)
}
clear
one
First, change your two function to actually return a value:
function two {
$var = "hey there"
return $var
}
and then update the one function to actually "capture" the output value by assigning it to a variable:
function one {
# PowerShell doesn't use parentheses when calling functions
$var = two
Write-Host "var is:" $var
}
If you really do want to reach up and change a variable in the scope of your caller, you can...
Set-Variable -Name $Name -Value $Value -Scope 1
It's a little meta/quirky but I've run into cases where it seemed proper. For example, I once wrote a function called RestoreState that I called at the beginning of several functions exported from a module to handle some messy reentrant cases; one of the things it did is reach up and set or clear $Verbose in the calling function's scope.
I have the following code that returns an array of array of results I try to use distinct-values to remove duplicates and it does nothing I have tried also removing using looping functions by comparing values with no success.
I have tried converting to "xs anyAtomicType" and using distinct values
I have tried putting in json array and extracting the sub-array
I have tried tokenizing, xdmp quote, string-before/after and many others
declare function local:verify-user-uri($dir as xs:string)
{
for $each in cts:uris($dir, ())
let $uIds := (for $d in $each
where contains($d, "/profile.xml")
return $d)
return $uIds
};
I get back duplicated result in form of:
/users/123-343-/profile.xml
/users/122-222-/profile.xml
/users/123-343-/profile.xml
/users/122-222-/profile.xml
/users/123-343-/profile.xml
/users/122-222-/profile.xml
I am expecting:
/users/123-343-/profile.xml
/users/122-222-/profile.xml
Is it possible that you have simply invoked this function 3 times and didn't realize it?
You have declared $dir to be a single xs:string. If your $dir happened to be a sequence of strings of the same directory, or if you otherwise invoked the function 3 times with the directory variable.
It can easily happen with function mapping enabled (default behavior). https://docs.marklogic.com/guide/xquery/enhanced#id_55459
There are a couple of things that you can do as a diagnostic:
1.) Remove the explicit type on the $dir parameter in the function:
declare function local:verify-user-uri($dir)
{
for $each in cts:uris($dir, ())
let $uIds := (for $d in $each
where contains($d, "/profile.xml")
return $d)
return $uIds
};
do you get an error executing cts:uris() that looks like this:
[1.0-ml] XDMP-ARGTYPE: )err:XPT0004) cts:uris(("/users/", "/users/", "/users/"), ()) -- arg1 is not of type xs:string?
2.) try disabling function mapping by adding the following to the prolog:
declare option xdmp:mapping "false";
and see if you then get an invalid coercion error like:
[1.0-ml] XDMP-AS (err:XPTY0004) $dir as xs:string -- Invalid coersion ("/users/", "/users/", "/users/") as xs:string
3.) You could also add something to the end of the sequence of values returned from the function to indicate how many times it has executed:
declare function local:verify-user-uri($dir as xs:string)
{
for $each in cts:uris($dir, ())
let $uIds := (for $d in $each
where contains($d, "/profile.xml")
return $d)
return $uIds, "#"
};
And see how many times you see "#" in the result. If more than one, you are invoking the function multiple times.
Next to the good suggestions from Mads, I notice a couple of other things about your code:
It doesn't make sense to iterate over $each as it contains one uri only. Keep in mind that a FLWOR statement ends with a return, which tells what should be the result per item
Beware that the first arg to cts:uris only marks a start, not an end. If you feed in /aaa/, you also get back /bbb/ etc, though not vice versa.
To be honest, I think you are looking for cts:uri-match() instead, which would reduce your function to a one-liner:
declare function local:verify-user-uri($dir as xs:string) {
cts:uri-match($dir || "*/profile.xml")
};
HTH!
PS: I do recommend always disabling function mapping as Mads recommends. It can prevent a lot of confusion.
How can I pass strings by reference to the parent scope?
This doesn't work since strings are not acceptable "values".
function Submit([ref]$firstName){
$firstName.value = $txtFirstName.Text
}
$firstName = $null
Submit([ref]$firstName)
$firstName
Error: "Property 'value' cannot be found on this object; make sure it exists and is settable"
Doing this doesn't give an error but it doesn't change the variable either:
$firstName = "nothing"
function Submit([ref]$firstName){
$firstName = $txtFirstName.Text
}
Submit([ref]$firstName)
$firstName
Edit:
Doing the first code block by itself works. However when trying to do it in my script it returns the error again. I fixed it enough for it to assign the variable and do what I want but it still throws up an error and I was wondering how to fix that. I think it's because it doesn't like variable;es changing during a running session. Here is a link to my script
https://github.com/InconspicuousIntern/Form/blob/master/Form.ps1
Your first snippet is conceptually correct and works as intended - by itself it does not produce the "Property 'Value' cannot be found on this object" error.
You're seeing the error only as part of the full script you link to, because of the following line:
$btnSubmit.Add_Click({ Submit })
This line causes your Submit function to be called without arguments, which in turn causes the $firstName parameter value to be $null, which in turn causes the error quoted above when you assign to $firstName.Value.
By contrast, the following invocation of Submit, as in your first snippet, is correct:
Submit ([ref] $firstName) # Note the recommended space after 'Submit' - see below.
[ref] $firstName creates a (transient) reference to the caller's $firstName variable, which inside Submit binds to (local) parameter variable $firstName (the two may, but needn't and perhaps better not have the same name), where $firstName.Value can then be used to modify the caller's $firstName variable.
Syntax note: I've intentionally placed a space between Submit and ([ref] $firstName) to make one thing clearer:
The (...) (parentheses) here do not enclose the entire argument list, as they would in a method call, they enclose the single argument [ref] $firstName - of necessity, because that expression wouldn't be recognized as such otherwise.
Function calls in PowerShell are parsed in so-called argument mode, whose syntax is more like that of invoking console applications: arguments are space-separated, and generally only need quoting if they contain special characters.
For instance, if you also wanted to pass string 'foo', as the 2nd positional parameter, to Submit:
Submit ([ref] $firstName) foo
Note how the two arguments are space-separated and how foo needn't be quoted.
As for an alternative approach:
[ref]'s primary purpose is to enable .NET method calls that have ref / out parameters, and, as shown above, using [ref] is nontrivial.
For calls to PowerShell functions there are generally simpler solutions.
For instance, you can pass a custom object to your function and let the function update its properties with the values you want to return, which naturally allows multiple values to be "returned"; e.g.:
function Submit($outObj){
$outObj.firstName = 'a first name'
}
# Initialize the custom object that will receive values inside
# the Submit function.
$obj = [pscustomobject] #{ firstName = $null }
# Pass the custom object to Submit.
# Since a custom object is a reference type, a *reference* to it
# is bound to the $outObj parameter variable.
Submit $obj
$obj.firstName # -> 'a first name'
Alternatively, you can just let Submit construct the custom object itself, and simply output it:
function Submit {
# Construct and (implicitly) output a custom
# object with all values of interest.
[pscustomobject] #{
firstName = 'a first name'
}
}
$obj = Submit
$obj.firstName # -> 'a first name'
Please try this out and see if you are getting the same results? It is working for me, and I really did not change much.
$txtFirstName = [PSCustomObject]#{
Text = "Something"
}
function Submit([ref]$var){
$var.value = $txtFirstName.Text
}
$firstName = $null
Submit([ref]$firstName)
$firstName
Can someone tell me, why this function call does not work and why the argument is always empty ?
function check([string]$input){
Write-Host $input #empty line
$count = $input.Length #always 0
$test = ([ADSI]::Exists('WinNT://./'+$input)) #exception (empty string)
return $test
}
check 'test'
Trying to get the info if an user or usergroup exists..
Best regards
$input is an automatic variable.
https://technet.microsoft.com/ru-ru/library/hh847768.aspx
$Input
Contains an enumerator that enumerates all input that is passed to a function. The $input variable is available only to functions and script blocks (which are unnamed functions). In the Process block of a function, the $input variable enumerates the object that is currently in the pipeline. When the Process block completes, there are no objects left in the pipeline, so the $input variable enumerates an empty collection. If the function does not have a Process block, then in the End block, the $input variable enumerates the collection of all input to the function.
Perhaps use a param block for parameters.
https://technet.microsoft.com/en-us/magazine/jj554301.aspx
Update: the problem seems to be fixed if you don't use $input as a parameter name, maybe not a bad thing to have proper variable names ;)
Also Powershell doesn't have return keyword, you just push the object as a statement by itself, this will be returned by function:
function Get-ADObjectExists
{
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
[string]
$ObjectName
)
#return result by just calling the object (no return statement in powershell)
([ADSI]::Exists('WinNT://./'+$ObjectName))
}
Get-ADObjectExists -ObjectName'test'
I'm using the Utl plugin for Vim and I'm looking for a method of creating a custom autocomplete function to generate a link to an id tag within the file. The format I want to use is:
:CommandName <file> <id tag in file>
I'd like the function to act like the standard directory completion for the first argument. For the second argument I'd like it to search the file specified in the first argument for all strings beginning with "id=" and return the values.
I've copied a similar function out of the main Utl package, but I've yet to get close to making it work, it currently looks like this:
fu! CompleteArgs(dummy_argLead, cmdLine, dummy_cursorPos)
" Split cmdLine to figure out how many arguments have been provided by user
" so far. If Using argument keepempty=1 for split will provide empty last
" arg in case of a new arg is about to begin or an non empty last argument
" in case of an incomplete last argument. So can just remove the last arg.
exe "echo \"cmdline:\" \"".a:cmdLine."\""
let utlArgs=split(a:cmdLine, '\s\+', 1)
execute "echo" string(utlArgs)
echo "echo" "test complete"
"remove the function name
call remove(utlArgs, -1)
" 1st arg to complete
if len(utlArgs)==1
return string(glob("*"))
endi
" 2nd arg to complete
if len(utlArgs)==2
"insert code here
endif
endfun
Has anyone got any ideas?
You can try out frawor. If you install it code will be the following:
execute frawor#Setup('0.0', {'#/fwc': '0.2',
\ '#/commands': '0.0',})
" No need to write bang here: above command will forbid script to be sourced
" twice, see :h frawor#Reload for how it can be updated.
function s:F.cmdfunc(file, tag)
" It will be called when the command launches. Alternatively you can replace
" `s:F.cmdfunc' in the below command.add call with a string you already had
" before. Note that you will have to replace s: in function names with <SID>
" and s:* variables will be just unaccessible.
endfunction
function s:F.gettags(file)
" This assumes that format is the same as used in ~/.vim/doc/tags. Note that
" if there may be any spaces, then you must escape them.
return map(readfile(a:file), 'matchstr(v:val, "\\v^.{-}\\t")[:-2]')
endfunction
" This replaces direct :command call, see :h frawor-f-command.add
call s:_f.command.add('CommandName', s:F.cmdfunc,
\{ 'nargs': '+',
\'complete': ['path in*F.gettags(#<)']})
While answering a very similar question, I have written a
complete function that determines the number of a command argument to
complete. Below is a version of that function, adapted to your case.
command! -nargs=* -complete=custom,FooComplete Foo echo [<f-args>]
function! FooComplete(arg, line, pos)
let l = split(a:line[:a:pos-1], '\%(\%(\%(^\|[^\\]\)\\\)\#<!\s\)\+', 1)
let n = len(l) - index(l, 'Foo') - 1
if n == 1
return string(glob('*'))
endif
return "1\n2\n3" " Replace this with appropriate id-completion logic.
endfunction
The function properly handles escaped whitespace (as a part of an
argument, not a separator) and whitespace before the command name.
Note that whitespace characters in suggestion candidates should be
escaped, otherwise a single argument would be treated by Vim as two
arguments.