为什么python不能执行通过stdin传递的zip存档?

时间:2013-11-28 23:20:39

标签: python compression zip python-2.6

我有一个包含__main__.py文件的zip存档:archive.zip

我可以用

执行它
python archive.zip
=> OK !

但不是

cat archive.zip | python
=> File "<stdin>", line 1
SyntaxError: Non-ASCII character '\x9e' in file <stdin> on line 2,
but no encoding declared; see http://www.python.org/peps/pep-0263.html for details

为什么两种模式之间存在差异?有没有办法让管道在没有解压缩到python之外的情况下工作?

我通过网络收到这个存档,并且希望在我收到它后尽快执行它,所以我认为拉链到python会起作用!

4 个答案:

答案 0 :(得分:6)

你可以'python file.zip'而不是'cat file.zip |的原因python'是Python内置了'zipimport',因此当你对文件运行python(或尝试导入它们)时,zipimport会在导入过程中对它们进行破解。 (有关详细信息,请参阅导入模块)。

但是使用stdin,python不会尝试搜索流数据 - 因为流数据可能是任何东西 - 可能是由代码处理的用户输入,可能是代码。没有办法知道,并且Python没有真正努力知道这个原因。

修改

偶尔,当你回答问题时 - 你会想'我真的不应该告诉别人答案',不是因为你希望保密或对他们持有一些权力。仅仅因为他们走下去的路径不是正确的道路,你想帮助他们走出他们正在挖掘的洞。这是其中一种情况。然而,根据我更好的判断,这是一种非常黑客的方式来完成类似于你想要的东西。这不是最好的方式,实际上可能是最糟糕的方式。

我刚刚和zipimporter玩了一会儿,并尝试了我能想到的所有技巧。我也看了'imp','compile'。到目前为止,我没有什么可以从内存中导入压缩模块(或蛋)。因此,需要一个临时步骤。

我会在前面说这个,我甚至不好意思发布这个。不要向与你合作的人或你尊重的人展示这一点,因为他们嘲笑这个可怕的解决方案。

这就是我的所作所为:

mkdir foo
echo "print 'this is foo!'" >>foo/__init__.py
zip foo.zip -r foo
rm -rf foo                   # to ensure it doesn't get loaded from the filesystem
mv foo.zip somethingelse.zip # To ensure it doesn't get zipimported from the filesystem

然后,我使用

运行此程序

cat somethingelse.zip | python script.py

#!/usr/bin/python 

import sys
import os
import zipfile
import StringIO
import zipimport
import time

sys.path.append('/tmp')

class SinEater(object):
    def __init__(self):
        tmp = str(int(time.time()*100)) + '.zip'
        f = open(tmp, 'w')
        f.write(sys.stdin.read(1024*64)) # 64kb limit
        f.close()
        try:
            z = zipimport.zipimporter(tmp)
            z.load_module('foo')

        except:
            pass

if __name__ == '__main__':
    print 'herp derp'
    s = SinEater()

产地:

herp derp
this is new

比这更好一百万倍的解决方案是拥有一个文件系统通知(inotify,kevent,无论使用什么窗口),它可以监视新zip文件的目录。当在该目录中删除新的zip文件时,您可以自动zipimport它。 但是,我无法强调即使解决方案也很糟糕。我对Ansible(实际上没什么)了解得太多,但我无法想象任何工程师都认为这对于如何处理代码更新或远程控制来说是一个很好的解决方案。

答案 1 :(得分:2)

<。> .zip文件由一系列文件组成,每个文件都是本地标头和压缩数据,后跟一个重复本地标头信息的中心目录,偏移到本地标头,以及其他一些允许随机的数据访问文件。

访问.zip文件的常用方法是在文件末尾找到中心目录并将其读入,然后使用该信息访问本地条目。这需要寻求。

可以编写一个从管道读取zip文件的解压缩。 (事实上​​I did that once。)然而,这不是Python用来读取zip文件的那种代码。

答案 2 :(得分:0)

有趣。我不知道这是可能的。但我会接受你的意见。

如果我猜测为什么它在从STDIN流入时不起作用,我会说这是因为处理ZIP存档通常需要向后搜索。 ZIP存档由一堆连接在一起的压缩文件组成(有足够的头数据可以独立解压缩),然后是最后的索引。根据我的经验,解压缩程序倾向于直接搜索索引然后在文件中更早地寻找有效负载数据(即使可以单独遍历压缩文件)。

由于在这种情况下,数据来自STDIN,解压缩程序无法向后搜索。同样适用于天真的网络流。

答案 3 :(得分:0)

有可能。 但需要一些编码) 主要思想是使用内存映射的临时文件并将其重定向到STDIN。

run_zipped_project.py

#!/usr/bin/env python
# encoding: utf-8
import os
import subprocess
from tempfile import SpooledTemporaryFile as tempfile

if __name__ == '__main__':
    filename = "test.zip" # here your zipped project
    size = os.path.getsize(filename)
    with open(filename, "rb") as test:
        code = test.read()
    test.close()

    # NOW WE LOAD IT FROM DISK BUT YOU CAN USE ANY ANOTHER SOURCE

    print "loaded {file} with size {size}".format(file=filename, size=size)
    size += 1  # prevent buffer overrun and dumping to disk


    f = tempfile(max_size=size, bufsize=size)
    f.write(code)
    f.seek(0)

    process = subprocess.Popen(["python2", "loader.py"],
        stdin=f,
        stdout=subprocess.PIPE,
        bufsize=size
        )
    print process.communicate()[0]
    f.close()
    print "closed"

loader.py

#!/usr/bin/env python
# encoding: utf-8
from zipimport import zipimporter

if __name__ == '__main__':
    zip = zipimporter('/dev/stdin')
    zip.load_module('__main__')