This is working in linux
sed "s/#\?\(PasswordAuthentication\s*\).*$/\1 no/" /etc/ssh/sshd_config;
But in MS Powershell in windows when I using
pwsh -c ssh root@161.97.108.95 -p 22 sed "s/#\?\(PasswordAuthentication\s*\).*$/\1 no/" /etc/ssh/sshd_config;
I get following error
sed: -e expression #1, char 35: unterminated `s' command
Before I fix encoding issue in powershell so \1 look right. So for now I can't solve this.
$OutputEncoding = [Console]::InputEncoding = [Console]::OutputEncoding = New-Object System.Text.UTF8Encoding
BodyName : utf-8
EncodingName : Unicode (UTF-8)
HeaderName : utf-8
WebName : utf-8
WindowsCodePage : 1200
IsBrowserDisplay : True
IsBrowserSave : True
IsMailNewsDisplay : True
IsMailNewsSave : True
IsSingleByte : False
EncoderFallback : System.Text.EncoderReplacementFallback
DecoderFallback : System.Text.DecoderReplacementFallback
IsReadOnly : True
CodePage : 65001
Edit more info
Is ssh in powershell th issue?
I see the command is changed in debug info
It seems that ssh
isn't capable of relaying arbitrary commands with separate arguments that contain \
characters to the remote shell, and simply escaping \
as \\
also does not work.
The solution is to pass the entire sed
command line as a single argument to ssh
:
# From PowerShell:
# Note the use of '...''...''...'
ssh root@161.97.108.95 -p 22 'sed ''s/#\?\(UsePAM\s*\).*$/\1 no/'' /etc/ssh/sshd_config'
Note:
Because PowerShell itself interpolates $
-prefixed tokens inside "..."
(expandable strings), it is better to use '...'
(verbatim strings).
'
characters inside '...'
by escaping them as ''
, as shown above.In cases where you do need up-front string interpolation by PowerShell, be sure to escape any pass-through $
characters as `$
, i.e. using the so-called backtick, PowerShell's escape character.
Note that \
has no special meaning in PowerShell, whereas it functions as the escape character in POSIX-compatible shells, for instance.
As an aside: $
chars. that aren't followed by an identifier (e.g., $foo
) or start a subexpression (e.g., $(1 + 2)
) are left untouched, so there is no strict need to `
-escape them, though it's probably better to do so for conceptual reasons; e.g., "5$/"
is treated the same as "5`$/"
by PowerShell and yields verbatim 5$/
($
sign retained).
The same applies analogously to POSIX-compatible shells (except that escaping $
requires \$
there).
Your question shows an attempt to call ssh
via pwsh -c
, i.e. via pwsh
, the PowerShell (Core) 7 CLI, which isn't necessary: just invoke ssh
directly, as shown above.
cmd.exe
, you'd use "..."
for enclosing the sed
command line as a whole (cmd.exe
only recognizes double-quoting, and doesn't interpret $
), and just '...'
for the embedded sed
script.Calling your command from Python, via subprocess.run()
:
As noted, there is no need to call via the PowerShell CLI.
From Python, you have to option to call ssh
directly, without involvement of a shell (be it PowerShell or cmd.exe
).
On Windows only, you may still pass a whole command line to execute, in which case the quoting rules are in essence the same as in cmd.exe
: only double-quoting ("..."
) is recognized.
Quoting in the case at hand gets tricky, because of the nested quoting that is necessary: embedded "..."
quoting around the pass-through sed
command, and, inside that, '...'
quoting around the sed
script. Additionally, the \
characters should be preserved as-is.
The simplest solution is to use a raw, triple-quoted string literal, r'''...'''
, inside of which '
, "
, and \
can all be embedded unescaped.
Therefore:
import subprocess
# Note: Shell-less invocation (`shell=False` is implied).
# Passing a whole command line as a single string in a shell-less
# invocation is supported on Windows only.
subprocess.run(
r'''ssh root@161.97.108.95 -v -p 22 "sed 's/#\?\(UsePAM\s*\).*$/\1 no/' /etc/ssh/sshd_config"''',
encoding="utf-8"
)