Saya memiliki skrip python yang menggunakan subprocess.check_call untuk meluncurkan Wine (Windows Emulator di Linux), lalu anggur meluncurkan Z:\\Program Files (x86)\\PeaZip\\peazip.exe.

Pertama, ketika saya menguji skrip python ini dalam mode debugging python3 -u -m ipdb unpack_archive.py, dan mengatur breakpoint di sekitar peluncuran anggur dan menjalankan pernyataan langkah demi langkah, Wine berhasil menjalankan peazip.exe. Artinya, peazip berhasil mengekstrak arsip PEA di Linux.

Namun, ketika saya menguji skrip python ini tidak dalam mode debugging python3 unpack_archive.py, maka saya menemukan peazip.exe tidak berhasil mengekstrak arsip PEA. Jadi saya menduga ada masalah sinkronisasi di wine atau python subprocess.check_call().

Sekarang solusi saya adalah, memasukkan time.sleep(1.0) setelah meluncurkan wine :

elif 'PEA archive' in ftype:
    if splitext(arcname)[1] != '.pea':
        tmpfile = os.path.join(tmpdir, basename(arcname))+'.pea'
    else:
        tmpfile = os.path.join(tmpdir, basename(arcname))
    shutil.copy(arcname, tmpfile)
    subprocess.check_call(["wine", "/home/acteam/.wine/drive_c/Program Files (x86)/PeaZip/peazip.exe",
        "-ext2here", to_wine_path(tmpfile)])
    import time
    time.sleep(1.0) # if we don't sleep, then peazip.exe won't extract file successfully 
    os.remove(tmpfile)
    copy_without_symlink(tmpdir, outdir)

Saya memeriksa manual anggur, tidak menyebutkan apa pun tentang sinkronisasi. Saya juga memeriksa subprocess.check_call(). Dokumen secara eksplisit mengatakan check_call() akan menunggu perintah selesai.

Saya tidak menginginkan solusi ini, karena jika file arsip PEA sangat besar, maka nilai batas waktu untuk sleep() harus lebih besar, dan kami tidak dapat memprediksi nilai batas waktu yang cukup sebelum menjalankannya.


Saya merujuk ke saran @jasonharper. Gunakan subprocess.check_output() alih-alih check_call()

    elif 'PEA archive' in ftype:
        if splitext(arcname)[1] != '.pea':
            tmpfile = os.path.join(tmpdir, basename(arcname))+'.pea'
        else:
            tmpfile = os.path.join(tmpdir, basename(arcname))
        shutil.copy(arcname, tmpfile)
        subprocess.check_output(["wine", "/home/acteam/.wine/drive_c/Program Files (x86)/PeaZip/peazip.exe",
            "-ext2here", to_wine_path(tmpfile)])
        os.remove(tmpfile)
        copy_without_symlink(splitext(tmpfile)[0], outdir)

Saya mengujinya dengan python3 unpack_archive.py Kevin.pea, yang merupakan arsip PEA 2.0GB. Proses ekstraksi memakan waktu 4 menit 16 detik. Tiga subfile berhasil dibongkar.

2
MikimotoH 11 Agustus 2017, 15:44

2 jawaban

Jawaban Terbaik

Pemahaman saya adalah bahwa wine yang dapat dieksekusi bukanlah emulator yang sebenarnya - itu hanya meluncurkan proses latar belakang yang disebut wineserver jika belum berjalan, memberitahunya untuk menjalankan program Windows, dan kemudian segera keluar dengan sendirinya - cukup mungkin sebelum program Windows mulai berjalan.

Salah satu jawaban untuk pertanyaan ini menyarankan agar menyalurkan output wine ke program lain akan menunda sesuatu sampai program Windows benar-benar keluar. Dalam istilah Python, ini akan setara dengan menggunakan check_output() alih-alih check_call(), meskipun saya sendiri belum mencobanya.

1
jasonharper 11 Agustus 2017, 15:05

Pertimbangkan untuk menggunakan penguncian penasehat untuk memblokir sampai proses telah keluar:

lockfile=open(tmpfile, 'a')
subprocess.check_call([
         "wine", "/home/acteam/.wine/drive_c/Program Files (x86)/PeaZip/peazip.exe",
        "-ext2here", to_wine_path(tmpfile)],
    preexec_fn=lambda: fcntl.flock(lockfile, fcntl.LOCK_EX),
    close_fds=False)
fcntl.flock(lockfile, fcntl.LOCK_EX)

Di sini, preexec_fn kami (dijalankan setelah kami fork()meninggalkan subproses tetapi sebelum wine dimulai) mengambil kunci, dan setelah check_call() kembali, kami kemudian coba ambil kunci itu sendiri -- yang akan memblokir jika belum dilepas.

(Perhatikan bahwa Anda harus memastikan bahwa wine tidak menutup deskriptor file itu sendiri sebelum program keluar; jika ya, salah satu cara untuk menghindarinya adalah dengan membuat kunci pada deskriptor yang diteruskan sebagai stdin, stdout, atau stderr) .

1
Charles Duffy 11 Agustus 2017, 15:15