I'm looking for the equivalent of the following bash syntax, but for the fish shell:
local datafile="${_Z_DATA:-$HOME/.z}"
i.e define a local variable that will take the value of $_Z_DATA if this one is define, or else will take the value of $HOME/.z
As far as I know, there is no syntax for this; you need something like
set datafile "$_Z_DATA"
test -z "$datafile"; and set datafile "$HOME/.z"
or
if set -q _Z_DATA; and test -n _Z_DATA
set datafile "$_Z_DATA"
else
set datafile "$HOME/.z"
fi
Unlike bash, variables defined inside a function are automatically local to that function, so no equivalent to local is needed. (A previous version of this answer used the -l option to localize the variable, but that makes it local to whatever block set occurs in.)
I would write this as:
set -q _Z_DATA; or set _Z_DATA $HOME/.z
on fish 3.0+ you can write:
set -q _Z_DATA || set _Z_DATA $HOME/.z
If you do this a lot you could define a function:
function defaultset --no-scope-shadowing
set -q $argv[1] || set $argv[1] $argv[2..-1]
end
# example usage
defaultset _Z_DATA $HOME/.z
I'm not a fish expert at all, but I wrote a helper function for this:
function default
for val in $argv
if test "$val" != ""
echo $val
break
end
end
end
The function echoes the first value that is not empty. (It works with both numbers and strings, too.)
~> set baz baz
~> default $foo $bar $baz $bat
baz
~> set bar bar
~> default $foo $bar $baz $bat
bar
To use in your script, you can call the function using the parentheses syntax:
~> set myfoo (default $foo "default")
~> echo $myfoo
default
~> set foo "the real foo"
~> set myfoo (default $foo "default")
~> echo $myfoo
the real foo
Returning to your specific example:
set datafile (default "$Z_DATA" "$HOME/.z")
Related
I have a function in Xonsh that I'm trying to use like a command (ie: without parens). The function should optionally take arguments, however whenever I call the function without arguments, I just get the function address. How do you call a function with optional parameters?
Here's the example:
def _up(args, stdin=None):
# go up any number of directories
if not args or len(args) < 1:
args[0] = 1
balloons = ('../' * int(args[0]))
# echo #(balloons)
cd #(balloons)
aliases['up'] = _up
When I call up with no parameters, I get <function __main__.up>. When I call it like this, it works: up 2.
I could do a function like this that works, but then I can't call it without using parentheses (ie: as a command) which is what I'd prefer:
def up(dirs=1):
# go up any number of directories
balloons = ('../' * dirs)
# echo #(balloons)
cd #(balloons)
Calling up() and up(2) both work this way, but is more cumbersome than just calling up or up 2. What's the right way to accomplish what I'm trying to do in Xonsh?
I'm not certain why you're getting the function repr when you aren't passing in an argument, but a tweaked version of your function works:
def _up(args): # don't need stdin
# go up any number of directories
if not args or len(args) < 1:
args = [1] # if args is None you can't index to it
balloons = ('../' * int(args[0]))
# echo #(balloons)
cd #(balloons)
aliases['up'] = _up
on current-ish main of xonsh # c2f862df this works to go up one level with just an up or you can specify many levels with up 3, etc.
I need to accept all kinds of global Jenkins variables as strings (basically as parameters to ansible like system - a template stored in \vars).
def proof = "\"${params.REPOSITORY_NAME}\""
echo proof
def before = "\"\${params.REPOSITORY_NAME}\""
echo before
def after = Eval.me(before)
echo after
The result is:
[Pipeline] echo
"asfd"
[Pipeline] echo
"${params.REPOSITORY_NAME}"
groovy.lang.MissingPropertyException: No such property: params for class: Script1
the first echo proves that the param value actually exists.
the second echo is the what the input actually looks like.
the third echo should have emitted asdf instead I get the exception.
Any ideas? I'm hours into this :-(
You may want to check:
groovy: Have a field name, need to set value and don't want to use switch
1st Variant
In case you have: xyz="REPOSITORY_NAME" and want the value of the parameter REPOSITORY_NAME you can simply use:
def xyz = "REPOSITORY_NAME"
echo params."$xyz" // will print the value of params.REPOSITORY_NAME
In case if your variable xyz must hold the full string including params. you could use the following solution
#NonCPS
def split(string) {
string.split(/\./)
}
def xyz = "params.REPOSITORY_NAME"
def splitString = split(xyz)
echo this."${splitString[0]}"."${splitString[1]}" // will print the value of params.REPOSITORY_NAME
2nd Variant
In case you want to specify an environment variable name as parameter you can use:
env.“${params.REPOSITORY_NAME}”
In plain groovy env[params.REPOSITORY_NAME] would work but in pipeline this one would not work inside the sandbox.
That way you first retrieve the value of REPOSITORY_NAME and than use it as key to a environment variable.
Using directly env.REPOSITORY_NAME will not be the same as it would try to use REPOSITORY_NAME itself as the key.
E.g. say you have a job named MyJob with the following script:
assert(params.MyParameter == "JOB_NAME")
echo env."${params.MyParameter}"
assert(env."${params.MyParameter}" == 'MyJob')
This will print the name of the job (MyJob) to the console assuming you did set the MyParameter parameter to JOB_NAME. Both asserts will pass.
Please don’t forget to open a node{} block first in case you want to retrieve the environment of that very node.
After trying all those solutions, found out that this works for my problem (which sounds VERY similar to the question asked - not exactly sure though):
${env[REPOSITORY_NAME]}
I would like to create a procedure like this simple example:
proc name {args} {
foreach val $args {
puts $val
}
}
But I would like the procedure to handle variables that don't exist, something like the code shown below:
proc name {args} {
foreach val $args {
if [info exists $val] {
puts $val
}
}
}
The problem is that the code is not executed because as soon as I call the procedure with an unexisting variable it immediately stalls, prior to go into the code, saying that there is a variable that doesn't exist. Is it probable because the procedure checks argument existance before entering the body?.
I can make it work by changing args by several optional variables with predefined values, but that limits the procedure and makes it look bad.
Can I make a proc able to handle unexisting variables?
You can't pass a variable as an argument: arguments have to be values. You can pass a variable name as an argument and use that as a reference to the variable inside the procedure. Example:
proc name args {
foreach varname $args {
upvar 1 $varname var
if {[info exists var]} {
puts $var
}
}
}
(The call to upvar creates a link between the variable whose name is the value of the variable varname outside the procedure and the variable called var inside the procedure. This is one way to "pass a variable to a procedure".)
Then you can do this:
% set foo 1 ; set baz 3
% name foo bar baz
1
3
Note that if you try to invoke the procedure as
% name $bar
where bar is undefined, the interpreter tries (and fails) to evaluate it before calling the procedure. That might be what you are seeing.
Documentation:
upvar
If we look at the point where you are calling the command (procedures are commands; they're a subclass really) you'll see something like this in your code:
name $a $b $c
That's fine if all those variables exist, but if one doesn't, it will blow up even before name is called. In Tcl, $a means exactly “read the variable a and use its contents here”, unlike in some other languages where $ means “look out language, here comes a variable name!”
Because of this, we need to change the calling convention to be one that works with this:
name a b c
That's going to require the use of upvar. Like this:
proc name {args} {
foreach varName $args {
# Map the caller's named variable to the local name “v”
upvar 1 $varName v
# Now we can work with v in a simple way
if {[info exists v]} {
puts $v
}
}
}
You made a mistake here
if [info exists $val]
When info exists is used it should be checked against variable name, not the variable value.
Lets come to your actual question.
You can pass the arguments to the procedure as a key-value pair, then it is pretty simple.
proc user_info {args} {
#Converting the arguments into array
if {[catch {array set aArgs $args}]} {
puts "Please pass the arguments as key-value pair"
return 1
}
#Assume, we need to ensure these 3 arguments passed for sure.
set mandatoryArgs "-name -age -country"
foreach mArg $mandatoryArgs {
if {![info exists aArgs($mArg)]} {
puts "Missing mandatory argument '$mArg'"
return 1
}
}
}
user_info -name Dinesh
I would like to load a module into a modulefile (to resolve dependencies).
MyModule:
#%Module########################################
##
## Modulefile
#
proc ModulesHelp { } {
puts stderr "Env for MyProg"
}
proc addPath {var val} {
prepend-path $var $val
}
module load MyOtherModule
addPath PATH /opt/MyModule/bin
MyOtherModule:
#%Module########################################
##
## Modulefile
#
proc ModulesHelp { } {
puts stderr "Env for MyOtherProg"
}
proc addPath {var val} {
prepend-path $var $val
}
addPath PATH /opt/MyOtherModule/bin
When I run module load MyModule, both modules seem to be loaded but environment is not right :
$module list
Currently Loaded Modulefiles:
1) MyModule 2) MyOtherModule
$echo $PATH
/opt/MyModule/bin:/usr/bin:/bin
If I add the line foreach p [array names env] { set tmp $env($p) } or at least set tmp $env(PATH) in the MyModule after the module load MyOtherModule line, the environment is correctly modified. It also work fine if I don't use my function addPath but I use the prepend-path command directly, which is a bit annoying because I would like to do more things in the addPath function of course.
Anyone as an idea on what is going on and what I am missing ?
The prepend-path is probably doing some “clever” stuff to manage a variable; what exactly it is is something I don't know and don't need to know, because we can solve it all using generic Tcl. To make your wrapping of it work, use uplevel to evaluate the code in the proper scope, though you need to consider whether to use the global scope (name #0) or the caller's scope (1, which is the default); they're the same when your procedure addPath is called from the global level, but otherwise can be quite different, and I don't know what other oddness is going on with the modules system processing.
To demonstrate, try this addPath:
proc addPath {var val} {
puts stderr "BEFORE..."
uplevel 1 [list prepend-path $var $val]
puts stderr "AFTER..."
}
We use list to construct the thing to evaluate in the caller's scope, as it is guaranteed to generate substitution-free single-command scripts. (And valid lists too.) This is the whole secret to doing code generation in Tcl: keep it simple, use list to do any quoting required, call a helper procedure (with suitable arguments) when things get complicated, and use uplevel to control evaluation scope.
(NB: upvar can also be useful — it binds local variables to variables in another scope — but isn't what you're recommended to use here. I mention it because it's likely to be useful if you do anything more complex…)
For a script that should be compatible to powerShell 2, I have a param called $exeLoc, declared as follows:
Param(
[parameter()]
[alias("el")]
$exeLoc= '......\sw' )
I try to set that parameter, from relative to absolute in a function, as follows:
Function FromRelToAbs()
{
Push-Location $exeLoc
$Global:exeLoc = (Join-path $PWD -ChildPath '\Vis.exe' )
Pop-Location
}
However, after calling the function above, the value of $exeLoc does not change.
The above code works perfectly on powersell v3 AND in powershell v2 ISE. It does not work properly on a powershell v2 window ( not ISE )
Any ideas ?
I take it your script is something along these lines:
param($x = 2)
write-output "[script] Value of x is 2"
function blah {
$global:x = 5
write-output "[blah] Setting x as 5"
}
Write-Output "Calling function blah"
blah
Write-Output "[script] Value of x is $x"
You set the variable in the script; and within the script is a function that refers to the variable in the global scope and sets its value.
If I try the above on my machine (PowerShell v2) it does set the variable correctly. Maybe I understood your script wrong?