Это работает в Linux
sed "s/#\?\(PasswordAuthentication\s*\).*$/\1 no/" /etc/ssh/sshd_config;
Но в MS Powershell в Windows, когда я использую
pwsh -c ssh [email protected] -p 22 sed "s/#\?\(PasswordAuthentication\s*\).*$/\1 no/" /etc/ssh/sshd_config;
Я получаю следующую ошибку
sed: -e выражение №1, символ 35: незавершенная команда `s'
Прежде чем я исправлю проблему с кодировкой в PowerShell, \1 выглядит правильно. Так что пока я не могу это решить.
$OutputEncoding = [Console]::InputEncoding = [Console]::OutputEncoding = New-Object System.Text.UTF8Encoding
Имя тела: utf-8
Имя кодировки: Юникод (UTF-8)
Имя заголовка: utf-8
Веб-имя: utf-8
WindowsCodePage: 1200
IsBrowserDisplay: True
IsBrowserSave: Правда
IsMailNewsDisplay: Верно
IsMailNewsSave: Верно
IsSingleByte: Ложь
EncoderFallback: System.Text.EncoderReplacementFallback
DecoderFallback: System.Text.DecoderReplacementFallback
IsReadOnly: Правда
Кодовая страница: 65001
Изменить дополнительную информацию
Есть ли проблема с SSH в PowerShell?
Я вижу, что команда изменена в информации об отладке.
@jdweng, хотя это правда, что нужно помнить об интерпретации $
PowerShell в строках "..."
, в данном случае это не проблема, потому что $
, за которым не следует подвыражение или имя переменной, сохраняется как есть. Таким образом, использование '...'
в данном случае не имеет значения, потому что "s/#\?\(PasswordAuthentication\s*\).*$/\1 no/" -eq 's/#\?\(PasswordAuthentication\s*\).*$/\1 no/'
— это $true
.
@mklement0: Я думаю, что вы ошибаетесь, и ОП должен заменить двойные кавычки одинарными. При использовании двойных кавычек люди всегда будут подвергать сомнению знак доллара, и нет никакого вреда в замене двойных кавычек на одинарные.
@jdweng, да, использование одинарных кавычек, несомненно, предпочтительнее, если следует исключить расширение строки (интерполяцию), как подразумевается в моем предыдущем комментарии. Моя точка зрения заключалась в том, что в данном конкретном случае это не имеет значения, поэтому это не решение. Другими словами: ваш комментарий должен был быть отступлением, четко сформулированным как таковой, а не претендовать на решение.
@mklement0 Мой точный вывод PowerShell 7.4.3 PS C:\Users\Tirsvad> ssh [email protected] -p 22 sed "s/#\?(UsePAM\s*).*$/\1 no/" /etc /ssh/sshd_config; sed: -e выражение №1, символ 19: незавершенная команда s' command PS C:\Users\Tirsvad> ssh [email protected] -p 22 sed 's/#\?\(UsePAM\s*\).*$/\1 no/' /etc/ssh/sshd_config; sed: -e expression #1, char 19: unterminated
s
Вам необходимо защитить обратную косую черту как от локальной, так и от удаленной оболочки. Попробуйте заключить sed -e "s/.../" filename
в одинарные кавычки (и разве последняя точка с запятой не является лишней?)
Похоже, что ssh
не способен передавать произвольные команды с отдельными аргументами, содержащими символы \
, в удаленную оболочку, и простое экранирование \
как \\
также не работает.
Решение состоит в том, чтобы передать всю командную строку sed
как один аргумент ssh
:
# From PowerShell:
# Note the use of '...''...''...'
ssh [email protected] -p 22 'sed ''s/#\?\(UsePAM\s*\).*$/\1 no/'' /etc/ssh/sshd_config'
Примечание:
Поскольку PowerShell сам интерполирует токены с префиксом $
внутри "..."
(расширяемые строки ), лучше использовать '...'
( дословные строки).
'
внутрь '...'
, экранируя их как ''
, как показано выше.В тех случаях, когда вам нужна предварительная интерполяция строк с помощью PowerShell, обязательно экранируйте любые сквозные символы $
как `$
, т. е. используйте так называемый обратный апостроф, escape-символ PowerShell .
Обратите внимание, что \
не имеет особого значения в PowerShell, тогда как он функционирует, например, как escape-символ в POSIX-совместимых оболочках.
Кстати: $
chars. за которыми не следует идентификатор (например, $foo
) или которые не начинают подвыражение (например, $(1 + 2)
), остаются нетронутыми, поэтому нет строгой необходимости экранировать их с помощью `
, хотя, вероятно, лучше сделать это по концептуальным причинам; например, "5$/"
обрабатывается PowerShell так же, как "5`$/"
, и возвращает дословно 5$/
(знак $
сохраняется).
То же самое относится и к POSIX-совместимым оболочкам (за исключением того, что для экранирования $
требуется \$
).
В вашем вопросе показана попытка вызвать ssh
через pwsh -c
, то есть через pwsh, интерфейс командной строки PowerShell (Core) 7, в чем нет необходимости: просто вызовите ssh
напрямую, как показано выше.
cmd.exe
, вы бы использовали "..."
для включения командной строки sed
в целом (cmd.exe
распознает только двойные кавычки и не интерпретирует $
) и только '...'
для встроенного скрипта sed
.Вызов вашей команды из Python через subprocess.run()
:
Как уже отмечалось, нет необходимости вызывать через PowerShell CLI.
Из Python у вас есть возможность вызывать ssh
напрямую, без участия оболочки (будь то PowerShell или cmd.exe
).
Только в Windows вы все равно можете передать на выполнение всю командную строку, и в этом случае правила кавычек, по сути, такие же, как и в cmd.exe
: распознаются только двойные кавычки ("..."
).
Цитирование в рассматриваемом случае становится затруднительным из-за необходимости вложенного кавычек: встроенного "..."
кавычек вокруг команды сквозного sed
и внутри него '...'
кавычек вокруг sed
скрипта. Кроме того, символы \
следует сохранить как есть.
Самое простое решение — использовать необработанный строковый литерал в тройных кавычках r'''...'''
, внутри которого '
, "
и \
могут быть встроены без экранирования.
Поэтому:
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 [email protected] -v -p 22 "sed 's/#\?\(UsePAM\s*\).*$/\1 no/' /etc/ssh/sshd_config"''',
encoding = "utf-8"
)
Спасибо за подробное объяснение. Теперь у меня есть только одна проблема: в моем скрипте Python нужно использовать subprocess.run(#Команда#) и получить тот же результат.
Рад слышать, что это помогло, @Tirsvad. Что касается Python: попробуйте "..."
заключить в кавычки всю sed
командную строку: subprocss.run('ssh [email protected] -p 22 "sed 's/#\?\(UsePAM\s*\).*$/\1 no/' /etc/ssh/sshd_config")
. Если это не поможет, я предлагаю создать новый пост с вопросами, посвященный именно этой проблеме.
Найдено решение для Python #test.py import subprocess cmd = r"pwsh -Command ssh [email protected] -v -p 22 'sed '\"'s/#\?\(UsePAM\s*\).*$/\1 no/'\"' /etc/ssh/sshd_config'" subprocess.run(cmd.replace('\\\\', '\\'), encoding = "utf-8")
Рад это слышать, @Tirsvad. Действительно, в своем давно удаленном комментарии я использовал обычные строковые литералы Python и не стал экранировать символы \
в них как \\
. Использование необработанного строкового литерала действительно предпочтительнее, а его форма в тройных кавычках (r'''...'''
) позволяет также вставлять '
и "
без экранирования, что обеспечивает более простое решение, не требующее ни вызова через pwsh -Command
, ни последующей обработки строки — см. обновлять.
Ошибка говорит, что опция «-e» не работает. В выражении sed есть знак доллара, который и вызывает проблему. Замените двойные кавычки одинарными, чтобы знак доллара воспринимался как символ, а не как имя переменной. sed может использовать любой символ в качестве начала/конца строки. Поэтому вместо кавычек можно использовать угловые скобки. Но здесь необходимо использовать одинарные кавычки, чтобы PS не заменял знак доллара.