簡體   English   中英

為什么 python 不能執行通過標准輸入傳遞的 zip 存檔?

[英]why can't python execute a zip archive passed via stdin?

我有一個包含__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

為什么這兩種模式之間存在差異,有沒有辦法使 pipe 工作而無需在 python 之外解壓縮?

我通過 .network 收到了這個存檔,並希望在收到它后盡快執行它,所以我認為將 zip 管道傳輸到 python 會起作用!

你可以'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(實際上沒什么)了解得太多,但我無法想象任何工程師都認為這對於如何處理代碼更新或遠程控制來說是一個很好的解決方案。

.zip文件由一系列文件組成,每個文件都是本地標頭和壓縮數據,后跟一個重復本地標頭信息的中心目錄,偏移到本地標頭,以及一些其他數據允許隨機訪問文件。

訪問.zip文件的常用方法是在文件末尾找到中心目錄並將其讀入,然后使用該信息訪問本地條目。 這需要尋求。

可以編寫一個從管道讀取zip文件的解壓縮。 (事實上我曾經做過一次 。)然而,這不是Python用來讀取zip文件的那種代碼。

有趣。 我不知道這是可能的。 但我會接受你的話。

如果我猜測為什么它從STDIN流入時不起作用,我會說這是因為處理ZIP存檔通常需要向后搜索。 ZIP存檔由一堆連接在一起的壓縮文件組成(有足夠的頭數據可以獨立解壓縮),然后是最后的索引。 根據我的經驗,解壓縮程序傾向於直接搜索索引,然后在文件中更早地尋找有效負載數據(即使可以單獨遍歷壓縮文件)。

由於在這種情況下,數據來自STDIN,因此解壓縮程序無法向后搜索。 同樣適用於天真的網絡流。

有可能的。 但需要一些編碼)主要思想是使用內存映射的臨時文件並將其重定向到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__')

如果你不需要cat命令,你可以做類似的事情嗎?

unzip -p archive.zip | python3

基本上你在將數據發送到 python 之前解壓縮到 stdout(-p 選項)!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM