У меня есть команда для получения времени последнего входа в систему определенного пользователя ubuntu, и мне нужен вывод, чтобы сохранить его в другом месте, поэтому, когда я запускаю это в моем скрипте python, я получаю синтаксическую ошибку, но когда я ssh на удаленном сервере и выполняю команда, нет проблем.
# last logged in time of ubuntu user
user_login = os.popen('ssh -i /Users/abcxyz/keypair ubuntu@1#.###.##.# lastlog -u 'ubuntu' | grep -v Latest | awk '{$1 = "";$2 = "";$3 = "";print $0 }'').read()
print(user_output)
когда я просто запускаю это в своем терминале, он отлично работает и дает мне результат:
ssh -i /Users/abcxyz/keypair ubuntu@1#.###.##.# lastlog -u 'ubuntu' | grep -v Latest | awk '{$1 = "";$2 = "";$3 = "";print $0 }'
Выход: Sat Nov 17 16:32:10 +0000 2018






Поскольку внутри вашей строки есть как одинарные, так и двойные кавычки, я бы заключил все в тройные кавычки
user_login = os.popen("""ssh -i /Users/abcxyz/keypair ubuntu@1#.###.##.# lastlog -u 'ubuntu' | grep -v Latest | awk '{$1 = "";$2 = "";$3 = "";print $0 }'""").read()
В противном случае, как написано, некоторые одинарные кавычки внутри вашей строки завершают строку полной длины посередине.
Идеально, вы должны сконструировать это так, чтобы вообще не подвергать вас проблеме, то есть позволить интерпретатору Python выполнить работу по генерации строки, заключенной в правильные кавычки (а затем повторно цитируя ее в смело переходите на ssh).
try:
from shlex import quote # Python 3
except ImportError:
from pipes import quote # Python 2
import subprocess
# specify your commands the way they're actually seen by the operating system -- with
# lists of strings as argument vectors.
rmt_pipeline = [[ 'lastlog', '-u', 'ubuntu' ],
[ 'grep', '-v', 'Latest' ],
[ 'awk', '{$1 = "";$2 = "";$3 = "";print $0 }' ]]
# ...then, let shlex.quote() or pipes.quote() determine how to make those lists be valid
# shell syntax, as expected by the remote copy of sh -c '...' invoked by ssh
rmt_pipeline_str = ' | '.join(' '.join(quote(word) for word in piece)
for piece in rmt_pipeline)
# ...finally, generate the argument vector for our local copy of ssh...
ssh_cmd = [ 'ssh', '-i', '/Users/abcxyz/keypair', rmt_pipeline_str ]
# and actually invoke it.
user_output = subprocess.Popen(ssh_cmd, stdout=subprocess.PIPE).stdout
Если вы В самом деле хотите использовать os.popen() - чего не следует, как документация Python явно предлагает использовать subprocess вместо - вы можете заменить последнюю строку на:
ssh_cmd_str = ' '.join(quote(word) for word in ssh_cmd)
user_output = os.popen(ssh_cmd_str)
В операционных системах семейства UNIX все выполнение программ происходит через системный вызов execve(), который передает список строк C. Указание этого списка самостоятельно дает вам максимально возможный контроль над тем, как происходит выполнение, и предотвращает атаки путем инъекции оболочки (когда пользователь, уполномоченный предоставить параметр одной из запущенных вами программ, передает содержимое, которое интерпретируется оболочкой как синтаксис, а не data, и вместо этого запускает совершенно другую программу или косвенную операцию).
Большое спасибо за то, что поделились кодом и подробным объяснением :) ценю это.
Идеально подходит для решения ближайшей проблемы; единственная причина, по которой я не предлагаю этого, заключается в том, что он оставляет открытыми другие проблемы (например, атаки с использованием инъекции оболочки), как только пользователь хочет параметризовать команду, которую он указывает таким образом.