Slack step does not treat "\n" as a newline when message generated by a Bash script step

script adding message to environment variable

	envman add --key SLACK_MESSAGE --value "foobar\nfoobar\nfoobar"

message property on slack step

	- message: |-
		$SLACK_MESSAGE

Build Log

Slack configs:
 - WebhookURL: *********
 - Channel: *********
 - FromUsername: Bitrise CI
 - FromUsernameOnError: Bitrise CI - ERROR
 - Message: foobar\nfoobar\nfoobar

Posted message

foobar\nfoobar\nfoobar

According to the slack documentation, I thought a \n is converted to a newline.
https://api.slack.com/docs/message-formatting#multiline_messages

1 Like

Confirmed, indeed neither

envman add --key SLACK_MESSAGE --value "foobar\nfoobar\nfoobar"

nor

echo "foobar\nfoobar\nfoobar" | envman add --key SLACK_MESSAGE

works, not even

envman add --key SLACK_MESSAGE --value "foobar \n foobar \n foobar"

But this does work:

#!/bin/bash
set -ex

envman add --key SLACK_MESSAGE --value "foobar
foobar
foobar"

An example working bitrise.yml for testing:

format_version: "3"
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
workflows:
  primary:
    steps:
    - script@1.1.3:
        inputs:
        - content: |-
            #!/bin/bash
            set -ex

            envman add --key SLACK_MESSAGE --value "some
            multi line
            slack message"
    - slack@2.4.1:
        inputs:
        - webhook_url: $SLACK_WEBHOOK_URL
        - channel: $SLACK_CHANNEL
        - message: $SLACK_MESSAGE

Thanks for reporting the issue @toshi0383, we’ll check if there’s an encoding/escaping issue in the CLI, or find an explanation.

In the meantime please see my workaround above, and if you have any questions just let us know!

Seems to be Bash related, not step or CLI one, most likely.

Using the exact same steps, just configuring the Script step to run a Go script instead of Bash (yes, the step supports this ;)), it works perfectly with:

package main

import (
	"fmt"
	"os"
	"os/exec"
)

func main() {
	c := exec.Command("bitrise", "envman", "add", "--key", "SLACK_MESSAGE", "--value", "foobar\nfoobar\nfoobar")
	cmdLog, err := c.CombinedOutput()
	fmt.Printf(" -> CMD OUT: %s\n", cmdLog)
	if err != nil {
		os.Exit(1)
	}
	os.Exit(0)
}


Full bitrise.yml:

format_version: "3"
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
workflows:
  primary:
    steps:
    - script@1.1.3:
        inputs:
        - content: |
            package main

            import (
              "fmt"
              "os"
              "os/exec"
            )

            func main() {
              c := exec.Command("envman", "add", "--key", "SLACK_MESSAGE", "--value", "foobar\nfoobar\nfoobar")
              cmdLog, err := c.CombinedOutput()
              fmt.Printf(" -> CMD OUT: %s\n", cmdLog)
              if err != nil {
                os.Exit(1)
              }
              os.Exit(0)
            }
        - runner_bin: go run
        - script_file_path: main.go
    - slack@2.4.1:
        inputs:
        - webhook_url: $SLACK_WEBHOOK_URL
        - channel: $SLACK_CHANNEL
        - message: $SLACK_MESSAGE

Thanks for confirmation!
I’ll use the bash hack version because it’s shorter than golang version.:innocent:

1 Like

Mystery solved. There’s no bug anywhere, this is how newline char interpretation works in Bash + Slack API.

So, what you have to know: \ + n is not the same as a newline character, usually represented by the \n escape sequence.

\ + n are two characters. In a string you can include \ + n, and in fact if you run the Bash script that’s what happens. It simply means \ and then a n char.

A newline character is a single character, which is usually represented by \n as an escape sequence, but the newline character is actually the ASCII code 10 char (see https://en.wikipedia.org/wiki/ASCII )

Bash does not treat \n as a newline char (as a single char), but most programming languages do when you specify \n in a String variable value (e.g. Go), that’s why \n worked in the Go script.

Don’t believe me? Try this script; simply save it into test.sh and then run bash test.sh to run it:

#!/bin/bash
set -ex

multiline_str="Multi\nline\n\nstring"
echo "$multiline_str"

You’ll get the exact same output as if you run this as a Script step in Bitrise config:

$ bash test.sh 

+ multiline_str='Multi\nline\n\nstring'
+ echo 'Multi\nline\n\nstring'
Multi\nline\n\nstring

While if you run this:

#!/bin/bash
set -ex

multiline_str="Multi
line

string"
echo "$multiline_str"

you get:

+ multiline_str='Multi
line

string'
+ echo 'Multi
line

string'
Multi
line

string

So, what’s the issue?

The thing what causes the confusion is that the Slack API docs mentions

You can post multiline messages through Slack’s APIs. Insert a newline by including the characters \n in your text. For example:

But they mean the \n escape sequence there, not \ + n as characters. As I said, whether \ + n are treated as two separate characters or as a single escape char for ASCII 10 depends on where you define it.

In Bash \n are two chars, while in Go for example that’s a single character.

This means that the solution I mentioned before with

is not a workaround, that’s exactly how the \n newline char can be represented in Bash when calling the Slack API.

If you’d have any questions @toshi0383 just let us know!


Edit: Slack step v2.5.0 now auto-converts the char sequence to the escape character, see Slack step does not treat "\n" as a newline when message generated by a Bash script step and Slack step does not treat "\n" as a newline when message generated by a Bash script step

Related StackOverflow discussion about Bash and \n :

That said, even if that is how it is expected to work (from a technical point of view) if you generate the message from Bash, it might not be obvious and indeed might be unexpected.

To make it easier to work with the step, I created a PR for the Slack step which will now auto-convert the \ + n chars in a string to the \n escape sequence.

https://github.com/bitrise-io/steps-slack-message/pull/17/commits/cf1b8c5dde84d2716cdc350b6b35bb1a346ab8ac#diff-7ddfb3e035b42cd70649cc33393fe32cR125

Auto-convert implemented in v2.5.0 of the step, will be available in the StepLib in a couple of minutes!

Thanks @toshi0383 for bringing this up and for all the infos!

Great! Thanks for clarification and step update!
Now it’s working intuitively.
But what if you want to actually write ‘’ + ‘n’ ? Maybe we want to opt-in or out that auto-convert behavior sometimes?

1 Like

We believe the current behaviour is what you’d expect so we don’t plan to add an option to opt-in or out, but if we’d ever receive a report suggesting that would be needed we’ll of course add that option.