如何摆脱命令中多个单引号引起的语法错误

How to get rid of syntax error that arises with multiple single quotation marks in the command

我有一个命令来获取某个 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 '{="";="";="";print [=10=] }'').read()

print(user_output)

当我在我的终端中 运行 时,它工作正常并给我输出:

ssh -i /Users/abcxyz/keypair ubuntu@1#.###.##.# lastlog -u 'ubuntu' | grep -v Latest | awk '{="";="";="";print [=11=] }'

输出: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 '{="";="";="";print [=10=] }'""").read()

否则如所写,您的字符串中的一些单引号将在中间终止全长字符串

理想情况下,您应该以一种完全不会让您遇到问题的方式来构造它——也就是说,让 Python 解释器完成生成正确 shell 引用字符串的工作(然后重新引用它以安全地传递给 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', '{="";="";="";print [=10=] }' ]]

# ...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文档explicitly suggests using subprocess instead——您可以将最后一行替换为:

ssh_cmd_str = ' '.join(quote(word) for word in ssh_cmd)
user_output = os.popen(ssh_cmd_str)

在 UNIX 系列操作系统上,所有程序的执行都是通过 execve() 系统调用进行的,它会传递一个 C 字符串列表。自己指定该列表可以让您最大程度地控制执行方式,并防止 shell 注入攻击(授权用户向您 运行 的其中一个程序提供参数的用户传递的内容是被 shell 解释为语法而不是数据,因此运行完全不同的程序或间接操作)。