我有一个python脚本,分布在几个文件中,都在一个目录中。我想将它作为单个可执行文件(主要用于Linux系统)分发,以便可以移动文件并轻松复制。
我已将主文件重命名为__main__.py
,并将所有内容压缩为myscript.zip
,因此现在我可以运行python myscript.zip
。但这一步太短了。我想以./myscript
运行它,而不创建别名或包装器脚本。
这有可能吗?最好的方法是什么?我认为也许zip文件可以嵌入到(ba)sh脚本中,将其传递给python(但如果可能的话,不要创建临时文件)。
编辑:
再过一次setuptools
(我之前没有设法让它工作),我可以创建一种自包含的脚本,一个" eggsecutable script"。诀窍在于,在这种情况下,您不能将主模块命名为__main__.py
。然后仍然存在一些问题:生成的脚本无法重命名,并且在运行时仍会创建__pycache__
目录。我通过在文件开头修改shell脚本代码并在那里将py -B
标志添加到python命令来解决这些问题。
编辑(2):也不是那么容易,因为我仍然在" eggsecutable"旁边有我的源.py
文件,移动一些东西然后停止工作。
答案 0 :(得分:1)
您可以将原始zip文件编辑为二进制文件,然后将shebang插入第一行。
#!/usr/bin/env python
PK...rest of the zip
当然,您需要一个适当的编辑器来处理二进制文件(例如:vim -b
),或者您可以使用一个小的bash脚本来创建它。
{ echo '#!/usr/bin/env python'; cat myscript.zip; } > myscript
chmod +x myscript
./myscript
答案 1 :(得分:0)
首先,有必要的"因此不是通常的事情,你确定你想这样做吗?"警告。那就是说,回答你的问题,而不是试图用别人认为你应该做的事来代替......
您可以编写脚本,并将其添加到python egg中。该脚本将从自身中提取蛋,显然在遇到egg文件数据之前调用exit。 Egg文件是可导入的但不可执行,因此脚本必须
python -m egg
抱歉,我现在正打电话,所以我稍后会用实际代码更新
答案 2 :(得分:0)
继续我自己的尝试,我编造了一些对我有用的东西:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import glob, os.path
from setuptools import setup
files = [os.path.splitext(x)[0] for x in glob.glob('*.py')]
thisfile = os.path.splitext(os.path.basename(__file__))[0]
files.remove(thisfile)
setup(
script_args=['bdist_wheel', '-d', '.'],
py_modules=files,
)
# Now try to make the package runnable:
# Add the self-running trick code to the top of the file,
# rename it and make it executable
import sys, os, stat
exe_name = 'my_module'
magic_code = '''#!/bin/sh
name=`readlink -f "$0"`
exec {0} -c "import sys, os; sys.path.insert(0, '$name'); from my_module import main; sys.exit(main(my_name='$name'))" "$@"
'''.format(sys.executable)
wheel = glob.glob('*.whl')[0]
with open(exe_name, 'wb') as new:
new.write(bytes(magic_code, 'ascii'))
with open(wheel, 'rb') as original:
data = True
while (data):
data = original.read(4096)
new.write(data)
os.remove(wheel)
st = os.stat(exe_name)
os.chmod(exe_name, st.st_mode | stat.S_IEXEC)
这将创建一个包含当前目录中所有* .py文件的轮子(除了它自己),然后添加代码以使其可执行。 exe_name
是文件的最终名称,from my_module import main; sys.exit(main(my_name='$name'))
应根据每个脚本进行修改,在我的情况下,我想从main
调用my_module.py
方法,接受参数my_name
(正在运行的实际文件的名称)。
不能保证它会在与创建它的系统不同的系统中运行,但是从源创建一个自包含的文件(例如放在~/bin
中)仍然很有用。
答案 3 :(得分:0)
另一个不太讨厌的解决方案(我很抱歉两次回答我自己的问题,但这不符合评论,我认为在单独的方框中更好)。
#!/usr/bin/env python3
modules = [
[ 'my_aux', '''
def my_aux():
return 7
'''],
['my_func', '''
from my_aux import my_aux
def my_func():
print("and I'm my_func: {0}".format(my_aux()))
'''],
['my_script', '''
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
import sys
def main(my_name):
import my_aux
print("Hello, I'm my_script: {0}".format(my_name))
print(my_aux.my_aux())
import my_func
my_func.my_func()
if (__name__ == '__main__'):
sys.exit(main(__file__))
'''],
]
import sys, types
for m in modules:
module = types.ModuleType(m[0])
exec(m[1], module.__dict__)
sys.modules[m[0]] = module
del modules
from my_script import main
main(__file__)
我认为这更为明确,尽管可能效率较低。所有需要的文件都包含在字符串中(为了提高空间效率,可以先将它们压缩并进行b64编码)。然后将它们作为模块导入,并运行main方法。应该注意以正确的顺序定义模块。