Search code examples
shellxpathxpath-2.0xidel

Is there a way to split a string in fixed width chunks in XPath?


Using xidel I'm extracting the //Assertion//Signature//KeyInfo//X509Certificate/text() from a SAMLResponse, this is a X509 certificate as a long base64 string.

I want to split this string into 64 chars blocks

I tried with tokenize() and replace() but I could make those work,

It seems that replace() does not allow me to use newlines \n in the replacement string:

echo "$SAMLRESPONSE" | base64 -D | xidel --xpath 'replace(//Assertion//Signature//KeyInfo//X509Certificate/text(),"(.{64})","$1\n")' -
**** Processing: stdin:/// ****
Error:
err:FORX0004: Invalid replacement: $1\n after $1\n
Possible backtrace:
  $000000010203F668: perhaps TXQTermTryCatch + 222920 ? but unlikely
  $0000000102068BBE: perhaps Q{http://www.w3.org/2005/xpath-functions}tokenize + 166350 ? but unlikely
  $000000010203FF78: Q{http://www.w3.org/2005/xpath-functions}replace + 376
  $0000000101FF853F: TXQTermNamedFunction + 767
  $0000000101F71CE7: perhaps ? ? but unlikely

Call xidel with --trace-stack to get an actual backtrace

And tokenize will treat the whole match as separator, and separator are not included in the output

echo "$SAMLRESPONSE" | base64 -D | xidel --xpath 'tokenize(//Assertion//Signature//KeyInfo//X509Certificate/text(),"(?:.{64})")' -
**** Processing: stdin:/// ****















XACcI5tcJbgsvr+ivGPos/WrhywkROwbEBh6OTNXTnaBiiIK

Is there any way to do split a string in fixed width chunks in XPath?


Solution

  • Your first idea wasn't wrong, you just have to use the codepoints-to-string function for generating the newline character:

    printf %s "$SAMLRESPONSE" |
    base64 -D |
    xidel --xpath '
        let
            $cert := //Assertion//Signature//KeyInfo//X509Certificate
        return
            "-----BEGIN CERTIFICATE-----" || codepoints-to-string(10) ||
            replace( $cert, ".{1,64}", "$0" || codepoints-to-string(10) ) ||
            "-----END CERTIFICATE-----" || codepoints-to-string(10)
    ' -
    

    note: I modified the regex to .{1,64} for making sure that the "replaced" string always ends with a linefeed


    ASIDE: In the first place, you don't even need to build the full output with XPath.

    {
        echo '-----BEGIN CERTIFICATE-----'
    
        printf %s "$SAMLRESPONSE" |
        base64 -D |
        xidel --xpath '//Assertion//Signature//KeyInfo//X509Certificate' - |
        fold -w 64
    
        echo '-----END CERTIFICATE-----'
    }