简体   繁体   English

为什么将参数插入双引号字符串时,Groovy 的 .equals() 在 jenkins 管道脚本中失败?

[英]Why does Groovy's .equals() fail in a jenkins pipeline script when interpolating parameters into a double quoted string?

(Note: I have looked at similar questions (such as this where the problem is failure to trim output from shell commands) but I think this case is distinct.) (注意:我看过类似的问题(例如问题是无法从 shell 命令修剪输出),但我认为这种情况是不同的。)

I have a pipeline script in Groovy that uses parameters (via properties([parameters([... ). When I interpolate the value of the parameter into a double quoted string, it fails a .equals() check against both captured (by which I mean "captured and .trim()d! ") stdout (which is my use case) and even a simple string literal.我在.equals()中有一个管道脚本,它使用参数(通过properties([parameters([... )我的意思是“捕获和 .trim()d! ”) 标准输出(这是我的用例),甚至是一个简单的字符串文字。

I can work around the problem using .trim() , (even though you can see, both through my echoing them and checking the .length() , that there is nothing to .trim() ), but I suspect that only "works" because it does an implicit .toString() --which is also a successful workaround.我可以使用.trim()解决该问题,(即使您可以通过我的回显和检查.length()看到.trim()没有任何内容),但我怀疑只有“有效" 因为它做了一个隐含的.toString()这也是一个成功的解决方法。

This looks like a bug to me, but it's literally my first week working with Groovy, so perhaps I'm missing something--can anyone explain what?这对我来说似乎是一个错误,但实际上这是我使用 Groovy 的第一周,所以也许我遗漏了一些东西——谁能解释一下?

Even a simple literal "foo" fails (ie "foo".equals("${params.foo_the_parameter}" ). Is the interpolated parameter some other kind of object or something?即使是简单的文字"foo" ”也会失败(即"foo".equals("${params.foo_the_parameter}" )。插值参数是其他类型的对象还是什么?

[ EDIT After getting the answer from @Matias Bjarland, I modified the code below to use println instead of shelled echo because it makes the output more concise. [编辑从@Matias Bjarland 得到答案后,我修改了下面的代码以使用 println 而不是 shelled echo,因为它使输出更简洁。 The solution he suggested is reflected in a commented block.]他建议的解决方案反映在注释块中。]

My groovy code:我的常规代码:

node() {
    properties([
        parameters([
            string(
                defaultValue: 'foo',
                description: 'This is foo',
                name: 'foo_the_parameter'
            )
        ])
    ])

    /* this is what I learned from the accepted answer
    bob="${params.foo_the_parameter}"
    println("class of interpolated param is ${bob.class}")
    simple_foo="foo"
    println("class of \"foo\" is ${simple_foo.class}")
    */
    echoed_foo = sh(script:"echo 'foo'", returnStdout: true).trim()
    println "echoed foo is [$echoed_foo], params foo is [${params.foo_the_parameter}]";
    echo_foo_length = echoed_foo.length()
    dqs_foo_length = "${params.foo_the_parameter}".length()
    println "their lengths are: echo: [$echo_foo_length] and dqs: [$dqs_foo_length]";
    if (echoed_foo.equals("${params.foo_the_parameter}")) {
        println "SUCCESS they are equals()"
    }
    else {
        println "FAIL they are not equals()" //this one fires
    }
    if (echoed_foo.equals("${params.foo_the_parameter}".trim())) {
        println "SUCCESS they are equals() after the dqs gets a trim()" //this one fires
    }
    else {
        println "FAIL they are not equals()after the dqs gets a trim()"
    }
    if (echoed_foo.equals("${params.foo_the_parameter}".toString())) {
        println "SUCCESS they are equals() after the dqs gets a toString()" //this one fires
    }
    else {
        println "FAIL they are not equals()after the dqs gets a toString()"
    }

    if ("foo".equals("${params.foo_the_parameter}")) {
        println "SUCCESS at least a simple literal \"foo\" works"
    }
    else {
        println "FAIL even a simple literal \"foo\" fails to be .equals() with the interpolated parameter" //this one fires
    }
}

Jenkins output:詹金斯输出:

Started by user Michael South
[Office365connector] No webhooks to notify
Obtained jenkins.groovy from git git@github.redacted.com:msouth/test_groovy_equals.git
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] node
Running on subnet_mon_02 in /opt/jenkins/m1/workspace/field-analytics-org/test_string_equals
[Pipeline] {
[Pipeline] properties
[Pipeline] sh
[test_string_equals] Running shell script
+ echo foo
[Pipeline] echo
echoed foo is [foo], params foo is [foo]
[Pipeline] echo
their lengths are: echo: [3] and dqs: [3]
[Pipeline] echo
FAIL they are not equals()
[Pipeline] echo
SUCCESS they are equals() after the dqs gets a trim()
[Pipeline] echo
SUCCESS they are equals() after the dqs gets a toString()
[Pipeline] echo
FAIL even a simple literal "foo" fails to be .equals() with the interpolated parameter
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
[Office365connector] No webhooks to notify
Finished: SUCCESS

In Groovy, you can simply use == to compare strings在 Groovy 中,您可以简单地使用==来比较字符串

In Java, you use String.equals() because str1 == str2 doesn't do what you'd expect: Java compares the references instead of the values.在 Java 中,您使用String.equals()是因为str1 == str2没有达到您的预期:Java 比较的是引用而不是值。

In Groovy, you can just write str1 == str2 and it does what you'd expect it to do.在 Groovy 中,您只需编写str1 == str2即可完成您所期望的工作。 Groovy compares the values using String.compareTo() and returns true when the result is 0 . Groovy 使用String.compareTo()比较值并在结果为0时返回true

GString g = "${'foo'}"
String s = "foo"

assert g == "foo" && s == "foo"
assert g instanceof GString && s instanceof String
assert !s.equals(g) && !g.equals(s)
assert g.compareTo(s) == 0 && s.compareTo(g) == 0
assert g == s && s == g 

Not sure if this is what you are hitting, but consider the following groovy code:不确定这是否是您要解决的问题,但请考虑以下 groovy 代码:

def x = 'World'
def gstr = "Hello ${x}!"
def str  = 'Hello World!'

println "class of gstr: ${gstr.class}"
println "class of str:  ${str.class}"

println(gstr.equals(str))
println(gstr.toString().equals(str))

which, when run prints:其中,运行时打印:

~> groovy solution.groovy
class of gstr: class org.codehaus.groovy.runtime.GStringImpl
class of str:  class java.lang.String
false
true

~> 

in other words, a string interpolation will result in an instance of groovy GString which is not necessarily equal to the string with the same content.换句话说,字符串插值将产生一个groovy GString的实例,它不一定等于具有相同内容的字符串。 Forcing evaluation using .toString() solves this particular problem.使用.toString()强制评估解决了这个特殊问题。

Quoting the groovy documentation on string interpolation :引用关于字符串插值的 groovy 文档

Any Groovy expression can be interpolated in all string literals, apart from single and triple single quoted strings.任何 Groovy 表达式都可以插入到所有字符串文字中,除了单引号和三重单引号字符串。 Interpolation is the act of replacing a placeholder in the string with its value upon evaluation of the string.插值是在评估字符串时用其值替换字符串中的占位符的行为。 The placeholder expressions are surrounded by ${} or prefixed with $ for dotted expressions.占位符表达式由 ${} 包围,或以 $ 为前缀以表示点分表达式。 The expression value inside the placeholder is evaluated to its string representation when the GString is passed to a method taking a String as argument by calling toString() on that expression.当 GString 通过在该表达式上调用 toString() 传递给以 String 作为参数的方法时,占位符内的表达式值被评估为其字符串表示形式。

In other words, you have to either assign the GString instance to a plan java String using some variation of:换句话说,您必须使用以下变体将 GString 实例分配给计划 java String:

String str1 = gstr
def str2 = gstr as String
def str3 = (String) gstr

, call a method which takes a String (which will coerce the GString to a string) or call gstr.toString() to force the conversion. ,调用一个接受字符串的方法(这会将 GString 强制转换为字符串)或调用gstr.toString()来强制转换。

Hope that helps.希望有帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM