Double quotes expansion conflict when using the Unix module

In Unix, to do something (for example, remove a file) remotely I use the following command :

ssh myusername@myremotehost "doSomething"

I want to be able to do it from Ocaml using the Unix module.
I tried something like this :

Sys.command "ssh myusername@myremotehost \"doSomething\" "

Unfortunately, this doesn’t work because the above command is interpreted
as ssh myusername@myremotehost doSomething (i.e. the double quotes are just removed). How to fix this ?

In my experiments, the problem is introduced by system(), not by the OCaml runtime. Here is a C program (try.c) that shows the same problem:

#include <stdlib.h>

int main()
{
    system("ssh myhost.com echo \"hello     world\"");
    return 0;
}

Session:

$ cc -o try try.c
$ ./try
hello world
$ ssh myhost.com "echo \"hello     world\""
hello     world

The spaces are preserved when running ssh directly from the shell, but are not preserved when running through system().

If you don’t need shell expansion functionality, you can use single quotes around doSomething. You probably know that, though. It’s not a general solution.

Using two layers of quotes does change the behavior that I see:

$ cat try2.c
#include <stdlib.h>

int main()
{
    system("ssh myhost.com echo \"'hello     world'\"");
    return 0;
}
$ cc -o try2 try2.c
$ ./try2
hello     world

And this works in OCaml also:

$ rlwrap ocaml
        OCaml version 4.03.0

# Sys.command "ssh myhost.com echo \"'hello      world'\"";;
hello      world
- : int = 0

You can use two layers of double quotes if you double escape the inner ones:

# Sys.command "ssh myhost.com echo \"\\\"hello      world\\\"\"";;
hello      world
- : int = 0

(This style of escaping gets out of hand quickly.)

Thank you jeffsco, your double layer of double quotes strategy is painful and ugly but sufficient for my purposes.

You can probably use Filename.quote for some situations and the {||} syntax for the string to avoid the escaping nightmare (Sys.command "ssh myhost.com echo \"\\\"hello world\\\"\"" would become something like Sys.command {|ssh myhost.com echo "\"hello world\""|}).

1 Like

This works and is a little tidier, cool.

In case it’s not obvious, the problem is that the string is interpreted by two different shells, one local (inside system()) and the other remote (inside ssh). So it takes two layers of quoting to protect spaces.

If you put single quotes outside of the inner double quotes, it might do what you want. You should need only single-backslashes.

Hello.

Just a side-note: I’m asking myself if a small quotation library would not be usefull.

It would allow to quote a string (just like Filename.quote), but:

  • would have different quotation schemes depending on what for the quotation is intended (I mean you don’t quote DOS shell command the same way as bash shell, by instance)
  • and allow to chain quotations

Salutations

Thanks for this feedback Khady, I was unaware of this {| |} syntax. Could you point me to where in the manual this syntax is explained, because I could~n’t find it

http://caml.inria.fr/pub/docs/manual-ocaml/extn.html#sec250

(Introduced in OCaml 4.02)

Quoted strings "{foo|...|foo}" provide a different lexical syntax to
write string literals in OCaml code. They are useful to represent
strings of arbitrary content without escaping -- as long as the
delimiter you chose (here "|foo}") does not occur in the string
itself.

\begin{syntax}
string-literal: ...
     |  '{' quoted-string-id '|'  ........ '|' quoted-string-id '}'
;
quoted-string-id:
     { 'a'...'z' || '_' }
;
\end{syntax}

The opening delimiter has the form "{id|" where "id" is a (possibly
empty) sequence of lowercase letters and underscores.  The
corresponding closing delimiter is "|id}" (with the same
identifier). Unlike regular OCaml string literals, quoted
strings do not interpret any character in a special way.

Example:

\begin{verbatim}
String.length {|\"|}         (* returns 2 *)
String.length {foo|\"|foo}   (* returns 2 *)
\end{verbatim}

Quoted strings are interesting in particular in conjunction to
extension nodes "[%foo ...]" (see \ref{s:extension-nodes}) to embed
foreign syntax fragments to be interpreted by a preprocessor and
turned into OCaml code: you can use "[%sql {|...|}]" for example to
represent arbitrary SQL statements -- assuming you have a ppx-rewriter
that recognizes the "%sql" extension -- without requiring escaping
quotes.

Note that the non-extension form, for example "{sql|...|sql}", should
not be used for this purpose, as the user cannot see in the code that
this string literal has a different semantics than they expect, and
giving a semantics to a specific delimiter limits the freedom to
change the delimiter to avoid escaping issues.
2 Likes

Another thing that may help (depending on your situation) is to use Unix.create_process where you can pass the argv array directly.

2 Likes