Using `system` with a long string VS multiple arguments

88 views Asked by At

I run the following shell command:

nvim +CheckHealth +'w ~/Desktop/file.txt' +qall

This calls nvim (Neovim) and tells it to run three commands in succession:

  1. CheckHealth to verify common errors. It runs in a buffer.
  2. w ~/Desktop/file.txt to save that same buffer to a file.
  3. qall to close all buffers.

I’m trying to run this from ruby, using system. If I run it as a single argument, it works fine:

system("nvim +CheckHealth +'w ~/Desktop/file.txt' +qall")

However, it fails (it runs but does not output a file) if ran as multiple arguments:

system("nvim", "+CheckHealth", "+'w ~/Desktop/file.txt'", "+qall")

What am I doing wrong? Note I am not asking for workarounds. I have a workaround, which is to run it as a single argument. My question is why doesn’t it work when ran as multiple arguments? What am I misunderstanding about system?

1

There are 1 answers

1
mu is too short On BEST ANSWER

When you use the single argument version of system:

system("nvim +CheckHealth +'w ~/Desktop/file.txt' +qall")

You're launching a shell and handing it that whole string:

nvim +CheckHealth +'w ~/Desktop/file.txt' +qall

to execute. That means that everything in that string will be interpreted by the shell; in particular, the shell will be handling the single quotes in +'w ~/Desktop/file.txt' and by the time vim gets to parse its argument list, it sees three arguments that look like this:

  1. +CheckHealth
  2. +w ~/Desktop/file.txt
  3. +qall

In the multi-argument version of system:

system("nvim", "+CheckHealth", "+'w ~/Desktop/file.txt'", "+qall")

no shell will be launched (a good thing as you don't have to worry about shell command injection and escaping) so the single quotes in the +w argument won't be removed by the shell. That means that vim sees these arguments:

  1. +CheckHealth
  2. +'w ~/Desktop/file.txt'
  3. +qall

Presumably vim isn't happy with the single quotes in the second argument.

Executive summary:

  • The single argument version of system uses a shell to parse the command line, the multi-argument version doesn't use a shell at all.

  • The single quotes in +'w ~/Desktop/file.txt' are there to keep the shell from treating that as two arguments, they're not there for vim.

  • If you're using the multi-argument version of system (which you should be doing), then you'd say:

    system("nvim", "+CheckHealth", "+w ~/Desktop/file.txt", "+qall")
    

    and not have to worry about quoting and escaping things to get past the shell.