I have a file that looks similar to this:

module.exports = {
    blank: {
        area: 'frontend',
        name: 'Magento/blank',
        locale: 'en_US',
        files: [
        dsl: 'less'
    backend: {
        area: 'adminhtml',
        name: 'Magento/backend',
        locale: 'en_US',
        files: [
        dsl: 'less'

I need to add another entry inside module.exports. I use vim inside a bash script in the following way:

        area: 'frontend',\r
        name: '$ESCAPED_THEME_PATH',\r
        locale: 'en_US',\r
        files: [\r
        dsl: 'less'\r

vim -c "%s/}\n/},$THEME_CONFIG\n/|wq" dev/tools/grunt/configs/themes.js

This works fine apart from the fact that I do not want to use \r as it seems to be breaking grunt. I would like to use \n instead, but if I replace \r in the code above with \n it does not work (THEME_CONFIG has no line breaks).

I have tried \\n, $'\n' but all I get is one line.

  • 3
    You should use the right tool for the right job and I think that here Vim isn't the right tool: in a non interactive bash shell you will most certainly have less trouble using sed (or maybe awk)
    – statox
    Apr 30 '19 at 10:21
  • 3
    Confusingly, \n in a search pattern is a newline, but when used in a replacement pattern, it is a NULL character. Why this is the case I do not know, but the docs do say to use \r or equivalently <CR> / ^M; the latter of which you can insert by pressing Ctrl+V Enter. Anyway, I agree with statox in that sed or awk would be a better choice for this, seeing as how you're only running a substitute command. You wouldn't have to deal with this quirk and could just use \n :)
    – ZeroKnight
    Apr 30 '19 at 10:44
  • 2
    This answer has a great explanation for why \n is NULL in replacements.
    – ZeroKnight
    Apr 30 '19 at 10:50

The problem isn't the \r characters in your string, but the fact that the string also contains literal newline characters, and Vim is including these in the replacement as NUL characters after the linebreaks. If you remove the latter from your bash script, then the replacement works correctly:

THEME_CONFIG="\r    $LOWERCASE_THEME_TITLE: {\r        area: 'frontend',\r        name: '$ESCAPED_THEME_PATH',\r        locale: 'en_US',\r [...]

If you (understandably) don't want to write your bash string on a single very long, line, there are a number of ways you can split it up.

Note that you’re also including another NUL character in your substitution which needs to be changed to a \r linebreak:

vim -c "%s/}\n/},$THEME_CONFIG\n/|wq" ...
                              NUL character

Here's a complete solution, using line continuations to break up the string over several lines:

"        area: 'frontend',\r"\
"        name: '$ESCAPED_THEME_PATH',\r"\
"        locale: 'en_US',\r"\
"        files: [\r"\
"            'css\/styles-m',\r"\
"            'css\/styles-l'\r"\
"        ],\r"\
"        dsl: 'less'\r"\
"    }\r"

vim -c "%s/}\n/},$THEME_CONFIG\r/|wq" themes.js
  • Actually I am. I had to go to the beginning of every line and hit backspace in order for grunt to stop complaining. May 1 '19 at 7:49
  • @KrzysztofWołowski Hmmm. What were you deleting from the file when you hit backspace? What's different between the incorrect file produced by Vim and the correct one produced by gawk?
    – Rich
    May 1 '19 at 8:28
  • @KrzysztofWołowski Ah, I see the problem, actually. Your string has newlines in it, as well as \r characters.
    – Rich
    May 1 '19 at 8:35
  • That may be the case but I remove \r from the string the output will be just one line. May 1 '19 at 8:51
  • @KrzysztofWołowski Don't remove the \r characters. Remove the newlines.
    – Rich
    May 1 '19 at 8:55

Thank you guys for the answers. awk (gawk in this particular case) is indeed the way to go. Though the solution has nothing to do with vim I paste it here just in case somebody stumbles upon this question.

        area: 'frontend',
        name: '$ESCAPED_THEME_PATH',
        locale: 'en_US',
        files: [
        dsl: 'less'

gawk -i inplace -v replacement="},\n$THEME_CONFIG" -v RS="" '{sub(/}\n/,replacement)}1' dev/tools/grunt/configs/themes.js  

Two methods to append something in ex mode. You don't need to add \r in these methods.

But in this case, you should use sed or awk, feel free to ignore this answer.


ex -sc +'if !search("}\\n") | q | endif' +'norm! A,' +"a|$THEME_CONFIG" +x theme.js
  • +'if !search("}\n") | q | endif' exit if search failed. :append doesn't work well with global or if, you need to exit immediately if search failed.
  • +'norm! A,' add , after }
  • +"a|$THEME_CONFIG" append a block of text below current line. I didn't add the ending ., but it still works.
  • +x save and exit

Characterwise paste

              v comma added here
        area: 'frontend',
        name: '$ESCAPED_THEME_PATH',
        locale: 'en_US',
        files: [
        dsl: 'less'
     ^ no line break between } and "

ex -ns +"let @\"=\"$THEME_CONFIG\"" +'g/}\n/ norm! $p' +x  term.js
  • -ns ignore recovery file, silent.
  • +"let@\"=\"$THEME_CONFIG\"" set default register to desired content. Because you want to add , directly after }, you must paste in characterwise mode, so you need to make sure THEME_CONFIG doesn't end in newline, otherwise your default register ended up in linewise mode. check :h :let-@.
  • +'g/}\n/ norm! $p' search, go to line end, paste. check :h :g.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.