Jenkins for loop exeucting even is empty list - jenkins

H. as i see when it is empty also it is printing empty. How to avoid the echo print if it is empty?
for (SubjectList in Subject.split('\r?\n'))
{
sh "echo '1${SubjectList }2'"
}

Try to use tokenize() instead:
for (SubjectList in Subject.tokenize('\r?\n'))
{
sh "echo '1${SubjectList }2'"
}
I don't know what \r?\n means but if you want to split it by newlines you can just use Subject.tokenize() (without parameters)
The split() method returns a string [] instance and the tokenize() method returns a list instance
tokenize() ,which returns a list, will ignore empty string (when a delimiter appears twice in succession) where as split() keeps such string.

Related

How to properly use PowerShell $args[]

Sometimes a PowerShell script gets called with arguments containing spaces and enclosed by double-quotes,
to ensure the string is interpreted as one single argument. A problem arises when the argument is empty and the double-quotes are passed containing nothing. PS sees the command line as non-empty, and tries to form an argument value from it. Except the double-quotes are stripped away, leaving an empty string as an argument value. Of course PS cannot actually use the empty value, so we want to validate command line arguments to disallow empty strings.
See the calling Cmd script, and the PS source, both commented. Tests 4 and 5 show the benefit of the function that scans the $args[] array and returns the first valid/non-empty argument.
Question: why can the function not be coded with just one ForEach loop?
Args.CMD:
#Echo Off
CD /D "%~dp0" & Rem Change to the folder where this script lives
CLS
Echo.
Rem PS script name is the same as this Cmd script
Set Script=%~n0.PS1
Rem Parameters of each call here are TestNumber, ExpectedExitCode, Argument1, Argument2
Rem Test 0: Try with zero arguments; expected exit code is 1
Call :TryMe 0 1
Rem Test 1: Try with non-useful arguments; expected exit code is 2
Call :TryMe 1 2 "" ""
Rem Test 2: Try with valid arguments, single words; expected exit code is 0
Call :TryMe 2 0 ArgOne ArgTwo
Rem Test 3: Try with valid arguments, multiple words; expected exit code is 0
Call :TryMe 3 0 "Arg One" "Arg Two"
Rem Test 4: Try with mixed arguments, single words; expected exit code is 0
Call :TryMe 4 0 "" ArgTwo
Rem Test 5: Try with mixed arguments, multiple words; expected exit code is 0
Call :TryMe 5 0 "" "Arg Two"
Echo All Done.
Pause
GoTo :EOF
:TryMe
Set TstNbr=%1
Set ExpCod=%2
Set Args12=%3 %4
Echo Test %TstNbr% of 5: PowerShell.EXE -F %Script% %Args12%
PowerShell.EXE -F %Script% %Args12%
Echo ErrorLevel is [%ErrorLevel%], expected was [%ExpCod%]
Pause
Echo.
GoTo :EOF
Args.ps1:
Set-StrictMode -Version 3.0 # disallow implicit variables
function ArgNonEmtFstGet( $arrArgs ) { # get the first non-empty argument
ForEach( $strArgs In $arrArgs ) { # <=== why is this double-indirection needed?
ForEach( $strArg In $strArgs ) { # scan all argument strings
$intArgLen = $strArg.Length # get length of current argument-string
If( $intArgLen -gt 0 ) { Return $strArg } # first time length is greater than zero, quit scanning and pass that argument back to caller
} # done scanning
} # <=== why is this double-indirection needed?
"" # when we finished scanning and did not return early, return a non-crashing value
} # ArgNonEmtFstGet
# Step 0: show we are alive
"Arg count: " + $args.Length # emit how many strings exist that *could be* arguments
# Step 1: look for any argument at all
If( $args.Length -eq 0 ) { # when there are zero strings that look like arguments
Write-Output "Putative argument required." # emit error message for 'no strings found'
Exit( 1 ) # abort with exit code
} # done with no strings found that *could be* arguments
"First putative argument: [" + $args[0] + "]" # emit the first argument-looking string
# Step 2: look for an argument that is actually meaningful
$strArg = ArgNonEmtFstGet( $args ) # get the first non-empty argument; that is our input
If( $strArg -eq "" ) { # when it is not a usable argument
Write-Output "Non-empty argument required." # emit error message for 'no real arguments found'
Exit( 2 ) # abort with exit code
} # done with no usable arguments found
"First non-empty argument: [$strArg]" # emit the first valid argument; this is our input
I can't reproduce your issue with your nested foreach-statement.
When I remove the inner loop I get the same result.
But I would suggest that you should avoid using the $args variable, because it accepts any input and doesn't allow named binding (only positional).
Just declare a param block in your PowerShell Script like below, then you don't need to loop over your arguments and additionally, you have named parameters you can use in your batch script. (Positional binding is still possible).
Sample script with param block and "ValidateScript" attribute, which validates the input to avoid null, empty or whitespace input.
param(
# first mandatory parameter. Default Position = 1 (if using positional binding)
[Parameter(Mandatory=$true)]
[ValidateScript(
{
if([String]::IsNullOrWhiteSpace($_)) {
throw [System.ArgumentException]'Your string is null, empty or whitespace!'
} else {
$true
}
}
)]
[string]
$varA,
# second mandatory parameter. Default Position = 2 (if using positional binding)
[Parameter(Mandatory=$true)]
[ValidateScript(
{
if([String]::IsNullOrWhiteSpace($_)) {
throw [System.ArgumentException]'Your string is null, empty or whitespace!'
} else {
$true
}
}
)]
[string]
$varB,
# You can just use this parameter if you want your arguments being absolutely dynamic
[Parameter(ValueFromRemainingArguments)]
[ValidateScript(
{
if([String]::IsNullOrWhiteSpace($_)) {
throw [System.ArgumentException]'Your string is null, empty or whitespace!'
} else {
$true
}
}
)]
[string[]]
$remainingArgs
)
# create ordered dictionary of mandatory arguments used for returning object
$properties= [ordered]#{
'Arg0' = "'$varA'"
'Arg1' = "'$varB'"
}
# add any optional remaining argument (if any)
$index = 1 # start index
foreach ($remainingArg in $remainingArgs) {
$index++
$properties.Add("Arg$index", "'$remainingArg'")
}
# return as object
[pscustomobject]$properties
As mentioned in the script, you can also just use the third parameter if you want your script being absolutely dynamic in terms of parameters.
More infos about attribute "ValidateScript": https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_advanced_parameters?view=powershell-5.1#validatescript-validation-attribute
PS:
Sample command when using named parameters in batch:
PowerShell.EXE -F %Script% -varA "%3" -varB "%4"

I have a Jenkins global variable in a string - how do I evaluate it?

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]}

Access project parameter in multi line shell script in jenkins pipeline [duplicate]

def a = "a string"
def b = 'another'
Is there any difference? Or just like javascript to let's input ' and " easier in strings?
Single quotes are a standard java String
Double quotes are a templatable String, which will either return a GString if it is templated, or else a standard Java String. For example:
println 'hi'.class.name // prints java.lang.String
println "hi".class.name // prints java.lang.String
def a = 'Freewind'
println "hi $a" // prints "hi Freewind"
println "hi $a".class.name // prints org.codehaus.groovy.runtime.GStringImpl
If you try templating with single quoted strings, it doesn't do anything, so:
println 'hi $a' // prints "hi $a"
Also, the link given by julx in their answer is worth reading (esp. the part about GStrings not being Strings about 2/3 of the way down.
My understanding is that double-quoted string may contain embedded references to variables and other expressions. For example: "Hello $name", "Hello ${some-expression-here}". In this case a GString will be instantiated instead of a regular String. On the other hand single-quoted strings do not support this syntax and always result in a plain String. More on the topic here:
http://docs.groovy-lang.org/latest/html/documentation/index.html#all-strings
I know this is a very old question, but I wanted to add a caveat.
While it is correct that single (or triple single) quotes prevent interpolation in groovy, if you pass a shell command a single quoted string, the shell will perform parameter substitution, if the variable is an environment variable. Local variables or params will yield a bad substitution.

Comparing 2 parameters in Jenkins pipeline in a single command

what is wrong with below code, comparing 2 strings in groovy
I am trying do the comparison between the 2 parameters in a single line to make it look tidier
if (params.dirname == ((params.path =~ ~/${params.dirname}/).with { matches() ? it[0] : null })) {
print success
}
Throwing Exception -
java.lang.NoSuchMethodError: No such DSL method 'matches' found among steps
There is no need to over-complicate your use case. According to:
params.dirname = hde, params.path = /usr/tmp/jenkins/hde/filename.txt or /usr/hde/jenkins/ing/filename.txt or any random path which has hde in it
you are trying to find if given string a contains substring b. It can be done using Java's method String.contains(String substring). Alternatively you can use regular expression for that, but String.contains() just looks a few times simpler to understand what is your intention. Consider following Groovy script:
def params = [
dirname: 'hde',
path: '/usr/tmp/jenkins/hde/filename.txt'
]
// Using String.contains()
if (params.path.contains(params.dirname)) {
println "Path '${params.path}' contains '${params.dirname}'"
}
// Using regular expression
if (params.path ==~ /(.*)${params.dirname}(.*)/) {
println "Path '${params.path}' contains '${params.dirname}'"
}
When you run it both if statements evaluates to true:
Path '/usr/tmp/jenkins/hde/filename.txt' contains 'hde'
Path '/usr/tmp/jenkins/hde/filename.txt' contains 'hde'

How to handle unexisting variables passed to a proc

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

Resources