2016-05-16 53 views
6

Jak mogę uzyskać subprocess.check_call, aby podać surowe wyjście binarne polecenia, wydaje się, że gdzieś jest ono niepoprawnie zakodowane.Python nie pobiera surowego binarnego z subprocess.check_call

Szczegóły:

Mam polecenie, która zwraca tekst tak:

some output text “quote” ... 

(cytaty te są unicode e2809d)

Oto jak dzwonię polecenie:

f_output = SpooledTemporaryFile() 
subprocess.check_call(cmd, shell=True, stdout=f_output) 
f_output.seek(0) 
output = f_output.read() 

Problem jest następujący:

>>> repr(output) 
some output text ?quote? ... 
>>> type(output) 
<str> 

(A jeśli wezwać "ord", "?" Otrzymuję 63.) Jestem na Pythonie 2.7 na Linux.

Uwaga: Uruchomienie tego samego kodu na OSX działa poprawnie dla mnie. Problem pojawia się, gdy uruchomię go na serwerze Linux.

+0

Możliwe, że wywoływany program dostosowuje swój sygnał wyjściowy w zależności od tego, co jest standardowe. Co powiesz na otwarcie zwykłego pliku i sprawdzenie, które bajty są faktycznie zapisane. BTW, 'SpooledTemporaryFile' jest po zabiciu. Część "buforowana" działa tylko dla rzeczy napisanych z Pythona. Po otrzymaniu deskryptora pliku zmieniono go na zwykły plik tymczasowy. Dodatkowy bufor StringIO nie był używany. – tdelaney

+1

Napisałem szybki program Pythona, który wypluwa ciąg znaków utf-8 i Twój program pracował dla mnie. – tdelaney

+0

Spróbuj uruchomić polecenie w powłoce i przekieruj do pliku. Jeśli masz zainstalowany 'vim', powinieneś także mieć' xxd', który może wyświetlać zrzut pliku hex. W twoim przykładowym tekście, wyjście utf-8 powinno wyglądać tak: '' '0000000: 736f 6d65 206f 7574 7075 7420 7465 7874 część tekstu wyjściowego 0000010: 20e2 809c 7175 6f74 65e2 809d 202e 2e2e ... cytat ... .. '' 'Lewy cytat to' e2 80 9c' a prawy cytat to 'e2 80 9d' –

Odpowiedz

1

Wow, to był najdziwniejszy problem, ale naprawiłem go!

Okazuje się, że program, do którego dzwonił (program java), zwracał różne kodowanie w zależności od tego, skąd został wywołany!

Dev osx maszyna, zwraca znaki dobrze, serwer Linux z linii poleceń, zwraca je dobrze, wywoływane z aplikacji Django, nie zmienia się w "?".

Aby rozwiązać ten problem skończyło się na dodanie tego argumentu do polecenia:

-Dfile.encoding=utf-8 

I got that idea here, i wydaje się działać. Istnieje również sposób na wewnętrzną modyfikację programu Java, aby to zrobić.

Niestety, obwiniam Pythona! Mieliście dobry pomysł.

+0

Czy próbowałeś naprawić swoje ustawienia narodowe ('locale.getpreferredencoding()'), jak zasugerowałem w mojej odpowiedzi (sprawdź je w tym samym kontekście co kod, który chcesz uruchomić)? – jfs

0

Przekierowanie (stdout=file) dzieje się na poziomie deskryptora pliku. Python nie ma nic wspólnego z tym, co jest zapisane w pliku, jeśli widzisz ? zamiast w samym pliku (nie w REPL).

Jeśli to działa na OS X i to „nie działa” na serwerze Linux to prawdopodobną przyczyną jest różnica w środowisku, należy sprawdzić LC_ALL, LC_CTYPE Lang envvars-python, /bin/sh (ze względu na shell=True), a cmd może wykorzystywać kodowanie regionalne, które jest ASCII, jeśli środowisko nie jest ustawione (C, POSIX locale).

aby "binarne" z podproces:

#!/usr/bin/env python 
import subprocess 

raw_binary = subprocess.check_output(['cmd', 'arg 1', 'arg 2']) 
print(repr(raw_binary)) 

Uwaga:

  • nie shell=True -Nie go używać, chyba że jest to konieczne
  • wiele programów może zmienić swoje zachowanie jeśli wykryją, że wyjście nie jest tty, example.