Open luozf14 opened 1 year ago
Same issue.
I just ran into this issue. Digging through the source code I think that the install script being written for bash is not the cause of the error per se. In the sense that, so long as the remote host has bash installed then the script should work fine, because we are explicitly invoking bash to run the install script:
I think the real cause of this specific error (starting with Unmatched '.
) is incompatible shell quoting syntax.
For context (at least this is my understanding), SSH can't directly pass arguments when running remote commands (a la child_process.spawn
); instead everything is run as a shell command (a la child_process.exec
, or sh -c
) using the default shell specified in the remote environment variable $SHELL
(that is, according to this StackOverflow discussion).
The code we use here accounts for that by manually shell-quoting/escaping the installServerScript
argument; that's what the
`bash -c '${installServerScript.replace(/'/g, `'\\''`)}'`
business is all about.
Here's where the problem lies: different shells have (subtly) different quoting/escaping rules. In POSIX-compatible shells, this works fine because single-quoted strings preserve every character literally, with the only exception of single-quotes, hence the '\\''
: close the string, then explicitly escape a single-quote-character, then reopen the string. Crucially, this includes newlines: multiline strings are OK in POSIX sh:
# this works just fine!
echo 'hello
world'
Unfortunately, tcsh is not POSIX-compatible. tcsh requires newlines to be escaped with \
in order to preserve them literally (see the "Lexical structure" section of the tcsh manpage). Thus
echo 'hello
world'
errors out with the message Unmatched '.
(in newer versions the error message might be Unmatched '''
? same idea though), which is exactly what we're seeing here. Instead, we'd have to do:
echo 'hello\
world'
There's one other discrepancy, which is that tcsh additionally allows "history substitution" inside single-quoted strings (again, see the tcsh man page); these begin with the special character !
, so as long as we escape those also then I think we're fine.
Also, since backslashes now have special meaning inside strings as the "escape" character, literal backslashes also need to be escaped.
It looks like there's a PR already (#132) to address this problem generally by allowing custom install scripts for different platforms, languages, etc. But a more localized/ad-hoc/temporary/easy workaround is to just fix the quoting without rewriting the entire install script. Provided we can detect what shell we're running in upon establishing the SSH connection, we can then apply the right quoting rules accordingly:
// POSIX-style
`bash -c '${installServerScript.replace(/'/g, `'\\''`)}'`
// tcsh-style
`bash -c '${installServerScript.replace(/'/g, `'\\''`).replace(/[!\n\\]/, '\\$&')}'`
This takes each special character inside a single-quoted string, i.e. \
!
and newlines, and inserts a backslash before it.
As an aside, notice that the install script itself is still written and running in bash; the only thing we're changing is the syntax of how we're quoting the install-script argument when invoking bash. Because that's the part that depends on the default environment in the SSH remote host.
I was trying to connect my server whose default shell is tcsh. The installation of open remote-ssh server was failed and gave me the following output.
It looks like the install script is written for bash. Any way to add support for tcsh?