为什么这个带有 #!/bin/sh 和在 4 个单引号内执行 Python 的片段能够工作?

18

我正在尝试理解这个问题的一个答案:

使用“#!/usr/bin/env python”不能传递参数给Python

#!/bin/sh
''''exec python -u -- "$0" ${1+"$@"} # '''

这个方法可以很好地工作,但是我不明白为什么需要在那一行的开头使用四个反引号而不是三个。

此外,为什么字符串末尾需要一个井号?


2
请注意,这将启动 $PATH 中的任何 Python,因此与 #!/usr/bin/env -i python 相比非常不安全,而且(更好的方法)是知道并使用完整路径:#!/usr/bin/python - Alois Mahdal
2个回答

33

Python支持三引号字符串:

'''something'''

Shell仅支持单引号字符串:

'something'
通过使用四个引号,sh 将其视为 2 个空字符串,但 Python 将前三个视为三引号字符串的开头,并将第四个包含在字符串值中。
然后,sh 将该行的其余部分解释为命令,但 Python 将其视为字符串的一部分。
对于sh来说,# 形成了注释,但对于 Python 来说,它仍然是一个字符串,并用闭合三引号结束。
因此,总结如下:
  • sh 看到:空字符串('' - 空字符串('' - 命令(exec python -u -- "$0" ${1+"$@"} - 注释(# '''
  • Python 看到:三引号字符串字面量(包含字符'exec python -u -- "$0" ${1+"$@"}#'
所以,sh 执行该命令,将自身替换为带有脚本名称和其余命令行参数的 python -u --,而 Python 读取此文件并仅看到不会消失的初始字符串文字。
由于它是文件中的第一个字符串文字,因此它将被设置为 __main__ 模块的文档字符串,但如果这是主要脚本,则几乎不会有任何影响。

1
${1+"$@"}是什么?它与$@有何不同? - Dog
@Dog:这意味着:如果参数1被设置,替换为 "$@"(带引号的参数),否则替换为null。 - Martijn Pieters
你可以在Python内部动态地执行这个操作,但那时候意义不大。 - Martijn Pieters
这里有更多关于 ${1+"$@"} 的上下文信息。因此,在许多情况下,"$@" 本身就可以正常工作。 - akhan
总之,这个技巧是利用Python的模块文档字符串来首先运行一个小型shell脚本。 - akhan

-2

我只是使用:

#!/bin/sh
''':'
exec python -tt "$0" "$@"
'''
# The above shell shabang trick is more portable than /usr/bin/env and supports adding arguments to the interpreter (python -tt)

使用您的解决方案相比接受的答案有什么优势? - akhan
2
这并没有回答问题,无论你使用什么,OP想要了解特定代码的工作原理。 - Martijn Pieters

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