Python Bash 命令:如何转义单引号?

3

我正在尝试通过Python执行一个shell命令。 命令类似于以下命令:

su -c "lftp -c 'open -u user,password ftp://127.0.0.1; get ivan\'s\ filename.pdf' " someuser

所以,当我尝试在Python中执行它时:

command = "su -c \"lftp -c 'open -u user,password ftp://127.0.0.1; get ivan\'s\ filename.pdf' \" someuser"
os.system(command)

或者:
command = subprocess.Popen(["su", "-c", "lftp -c 'open -u user,password ftp://127.0.0.1; get ivan\'s\ filename.pdf'", "someuser"])

我遇到了以下错误:

bash: -c: line 0: unexpected EOF while looking for matching `''
bash: -c: line 1: syntax error: unexpected end of file

涉及:ivan的单引号。

我知道在其中有很多单/双引号,但我该如何转义它们?

提前致谢!

编辑:这对我有用:

subprocess.call(["su","-c",r"""lftp -c "open -u user,password ftp://127.0.0.1; get ivan\'s\ filename.pdf" """, "someuser"])

非常感谢大家!

3
请使用subprocess.call()函数,将你的单独参数作为一个列表传递,无需加任何引号 - Python将为您正确转义您的shell。 - zwer
你可能想使用curl而不是lftp... 你可以通过pycURL或调用命令来实现。 - Gert van den Berg
我尝试使用以下代码来实现:subprocess.Popen(["su", "-c", "open -u user,password ftp://127.0.0.1; get ivan's\ filename.pdf", "someuser"]),但仍然出现错误。 - idiaz01
@zwer:问题在于su -c,这会导致由su启动的shell进行不可避免的shell转义处理。 - Gert van den Berg
为什么要使用 su 命令?脚本是以普通用户还是以 root 用户运行?获取当前用户的文件,然后移动并更改所有权可能更加优雅... - Gert van den Berg
2个回答

3
如果您打印测试字符串,您会注意到它的结果如下:
su -c "lftp -c 'open -u user,password ftp://127.0.0.1; get ivan's\ filename.pdf' " someuser

问题在于您需要转义用于转义单引号的斜杠,以防止Python将其解释为字符。
command = "su -c \"lftp -c 'open -u user,password ftp://127.0.0.1; get ivan\\'s\\ filename.pdf' \" someuser"

当你使用反斜杠时,lftp会报错。你可以尝试以下方法:

正确的写法:

command = "su -c \"lftp -c \\\"open -u user,password ftp://127.0.0.1; get ivan\\'s\\ filename.pdf\\\" \" someuser"

(它使用(转义)双引号,以确保由su启动的shell仍解释转义序列)

os.system(a)实际上执行subprocess.call(["sh",“-c”,a]),这意味着sh看到su -c“lftp -c'open -u user,password ftp://127.0.0.1; get ivan's\ filename.pdf'” someuser (对于原始代码)。 它会对此进行转义序列处理,并看到未关闭的单引号(最初由ivan'关闭),从而导致错误。 修复后,sh调用su,su又启动另一个实例sh进行更多的转义处理,从而导致lftp的错误(因为sh不处理单引号中的转义序列)

subprocess.call()curl是更好的实现方式 - curl需要更少的转义,您可以在命令行上使用curl“ftp://user:password@127.0.0.1/ivan's filename.pdf”,通过su -c和python需要更多的转义。使用sudo而不是su也需要更少的转义....

如果要使用subprocess.call()(删除一层shell),可以使用

subprocess.call(["su","-c","lftp -c \\\"open -u user,password ftp://127.0.0.1; get ivan\\'s\\ filename.pdf\\\"", "someuser"])

问题在于Python只处理一层转义,而从su调用的sh -c则有下一层...这会导致一个相当丑陋的命令...(使用不同的引号可能会稍微减少这种情况...) 使用r""可以摆脱Python级别的转义处理:(只需要Shell级别的转义)(使用三重引号允许字符串中出现引号)
subprocess.call(["su","-c",r"""lftp -c \"open -u user,password ftp://127.0.0.1; get ivan\'s\ filename.pdf\"""", "someuser"])

添加一个空格可以去掉Shell转义字符,因为lftp似乎不需要对带有空格和单引号的文件名进行转义。
subprocess.call(["su","-c",r"""lftp -c "open -u user,password ftp://127.0.0.1; get ivan's filename.pdf" """, "someuser"])

这将导致最终的lftp ARGV。
["lftp","-c","open -u user,password ftp://127.0.0.1; get ivan's filename.pdf"]

对于curl(由于涉及到su),仍然会出现问题:

subprocess.call(["su","-c",r"""curl "ftp://user:password@127.0.0.1/ivan's filename.pdf" """, "someuser"])

谢谢!对我来说,r""这样使用是有效的:subprocess.call(["su","-c",r"""lftp -c "open -u user,password ftp://127.0.0.1; get ivan\'s\ filename.pdf" """, "someuser"]) - idiaz01

1
使用subprocess.call()是执行此任务的最佳且更安全的方法。
以下是来自文档页面的示例:
subprocess.call(["ls", "-l"]) # As you can see we have here the command and a parameter

关于错误,我认为它与空格和 ' 字符有关。尝试使用 字符串字面值(注意在字符串前加上 r ,还要确保命令与您在 BASH 中使用的完全匹配):
r"My ' complex & string"

所以,在你的情况下:
command = subprocess.Popen(["su", "-c", r"lftp -c 'open -u user,password ftp://127.0.0.1; get ivan's filename.pdf'", "someuser"])

问题出在第三个命令上,我有一个带有另一个命令和单引号的lftp -c。 - idiaz01
我在最后添加了一条关于@idiaz01(字符串字面值和使用r"")的注释。 - Pitto
问题是su启动了另一个shell,该shell进行了另一层的转义处理...你还缺少lftp... - Gert van den Berg
1
我们被迫使用su吗? 脚本用户是否可以启用此命令,而不是使用root? 从安全角度来看,这也将是最佳选择。 - Pitto
@Pitto: sudo可能也能避免额外的转义层,因为它允许使用命令和参数...(不过它的配置更复杂一些...)(其他选项包括使用SSH进入具有在authorized_keys文件中允许脚本运行的密钥的用户...) - Gert van den Berg
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接