58

I need to properly escape single and double quotes in an ansible playbook in order to set the environment variable. None of this works:

  - name: Set environment variable
    command: >
      export EXTRA_CONFIG=“'”{"client": {"subscriptions": ["DIND-Worker"], "cluster": "internal"}}“'”

  - name: Set environment variable
    command: >
      export EXTRA_CONFIG=''{"client": {"subscriptions": ["DIND-Worker"], "cluster": "internal"}}''

  - name: Set environment variable
    command: >
      export EXTRA_CONFIG=''{\"client\": {\"subscriptions\": [\"DIND-Worker\"], \"cluster\": \"internal\"}}''

Looked at this:

http://yaml.org/spec/current.html#id2532720

https://github.com/dotmaster/toYaml/issues/1

The error message I get is:

fatal: [ip.address]: FAILED! => {"changed": false, "cmd": "export 'EXTRA_CONFIG={\"client\":' '{\"subscriptions\":' '[\"DIND-Worker\"],' '\"cluster\":' '\"internal\"}}'", "failed": true, "msg": "[Errno 2] No such file or directory", "rc": 2}
Anthon
  • 69,918
  • 32
  • 186
  • 246
user3081519
  • 2,659
  • 5
  • 25
  • 35
  • Can you try using the `shell` instead of the `command` module? You can then simply escape through `\"`. If you just need the env vars on a per task basis you could also consider using `shell`'s `environment` feature as described [here](http://stackoverflow.com/questions/31775099/how-to-set-environmental-variables-using-ansible) – fishi0x01 May 25 '16 at 08:47

3 Answers3

73

> starts a block scalar, in which you do not need to escape anything at all (and there are no escape sequences processed). So assuming you want single quotes around your JSON-like value, just do:

  - name: Set environment variable
    command: >
      export EXTRA_CONFIG='{"client": {"subscriptions": ["DIND-Worker"], "cluster": "internal"}}'

Edit: Also be aware that a folded scalar by default includes a trailing newline character. If you do not want to have this, just use >- instead of >.

flyx
  • 35,506
  • 7
  • 89
  • 126
  • Does this actually work for you? In my case I'd still get `[Errno 2] No such file or directory`. Is `export` possible through the `command` module? – fishi0x01 May 25 '16 at 09:35
  • 3
    Well I answered this based on my knowledge of YAML; I do not know ansible. But I would guess that the command is executed in an isolated shell instance, so exporting anything here will not affect the system at all. If you want to set a global environment variable, you have to put it in `/etc/environment` or something. – flyx May 25 '16 at 09:39
  • Good point - you are right, each task would open a new shell, which means the env var is not persistent across the whole play except you place it in some dedicated file on the node which is sourced by the shell or you explicitly set it for each task, e.g., using the `environment` feature of ansible's `shell` module. – fishi0x01 May 25 '16 at 10:42
  • This worked for me, but I don't understand how can I use `>-`, could you give an example? Is it actually just trimming the trailing whitespace/empty lines? – maricn Nov 24 '17 at 10:36
  • 1
    @maricn You just replace the `>` in the example above with `>-`. Without the `-`, `>` already trims trailing empty lines, but does keep the final newline character at the end. The `-` just removes that final newline character. There is also `>+` for when you want to avoid trimming *any* trailing empty lines. – flyx Nov 24 '17 at 11:34
10

You are using folded style scalars (introduced by >) and according to the YAML 1.2 specification you cannot escape characters:

Folded scalars:

The folded style is denoted by the “>” indicator. It is similar to the literal style; however, folded scalars are subject to line folding.

And the relevant text wrt escaping in literal style scalars.

Inside literal scalars, all (indented) characters are considered to be content, including white space characters. Note that all line break characters are normalized. In addition, empty lines are not folded, though final line breaks and trailing empty lines are chomped.

From your example it is unclear what you really want to do. You should probably drop folding style in favour of double quoted style:

The double-quoted style is specified by surrounding “"” indicators. This is the only style capable of expressing arbitrary strings, by using “\” escape sequences. This comes at the cost of having to escape the “\” and “"” characters.

or single quoted style:

The single-quoted style is specified by surrounding “'” indicators. Therefore, within a single-quoted scalar, such characters need to be repeated. This is the only form of escaping performed in single-quoted scalars. In particular, the “\” and “"” characters may be freely used. This restricts single-quoted scalars to printable characters. In addition, it is only possible to break a long single-quoted line where a space character is surrounded by non-spaces.

So you should first decide what the output should be exactly, then if you need to escape any characters with backslash. If you don't you can just use folded style without any escaping, or single quoted style by escaping the ', or double quoted style by escaping " and any \. If you need \ escaping double quoted style is your only option.

Anthon
  • 69,918
  • 32
  • 186
  • 246
  • Can you clarify your phrase? `If you need \ escaping double quoted style is your only option.` With single quotes, you don't need to escape `\`. Why are single quotes not an option if you need to include `\` in a string? – x-yuri Feb 16 '18 at 18:29
  • If you need `\` escaping means if you need to insert one of the [escaped characters](http://www.yaml.org/spec/1.2/spec.html#id2776092) that need a backslash. You cannot put those in single quoted scalar strings. In that context talking about single quotes are irrelevant. – Anthon Feb 16 '18 at 22:12
  • I see. You mean if one needs to add things like `\n`, `\t`, he has to use double quotes. In single quotes backslash doesn't have any special meaning. Neither does escape sequences like `\n`, `\t`, and so on. – x-yuri Feb 16 '18 at 22:57
0

I don't think this has anything to do with escaping. From the docs for command:

The command module takes the command name followed by a list of space-delimited arguments. The given command will be executed on all selected nodes. It will not be processed through the shell, so variables like $HOME and operations like "<", ">", "|", and "&" will not work (use the shell module if you need these features).

The command you're trying to run, export, isn't an executable; it's a shell builtin. It makes sense that if Ansible isn't running the command through a shell that shell builtins aren't available, ergo "No such file or directory."

Like the docs say, you could use shell instead of command, but I'm not sure this is a solution, because Ansible probably doesn't run subsequent commands in the same shell, so any environment variables you set previously will be absent. See the answers to this question for some options that are more likely to work: How to set linux environment variables with ansible

Community
  • 1
  • 1
Jordan Running
  • 102,619
  • 17
  • 182
  • 182