2017-04-08 67 views
6

Python'dan bir program aramak istiyorum ve Python'un işlem stdout'u bir boruya bağlandığında bile stdout'un tty olduğunu düşünün. Bu yüzden gelen doğrulanabilir hangi Bunu başarmak için pty.spawn fonksiyonunu kullanılan aşağıdaki: Python pty.spawn stdin yankılanmadı ancak master'ın stdout'una yeniden yönlendirildi

$ python -c "import sys; from subprocess import call; call(sys.argv[1:])" python -c "import sys; print sys.stdout.isatty()" | cat 
False 

$ python -c "import sys; import pty; pty.spawn(sys.argv[1:])" python -c "import sys; print sys.stdout.isatty()" | cat 
True 

(Biz kökenli süreç Stdout'a düşünerek ayartılır yani ikinci komuta biz hedefimize ulaşmış olduğunu görüyoruz

bir tty'dir.)

Ancak sorun şu ki, pty.spawn kullanıyorsak, girdisi yankılanmıyor, daha ziyade master'ın stdout'una yönlendiriliyor. Bu aşağıdaki komutla görülebilir:

$ python -c "import sys; import pty; pty.spawn(sys.argv[1:])" cat > out.txt 
$ # Typed "hello" in input, but that is not echoed (use ^D to exit). It is redirected to output.txt 
$ cat out.txt 
hello 
hello 

(Ama bu sorun onun Stdin ve stdout'u doğru usta bağlıdırlar beri subprocess.call

$ python -c "import sys; from subprocess import call; call(sys.argv[1:])" cat > out1.txt 
hello 
$ cat out1.txt 
hello 

kullandığınızda da mevcut değil.)

Bir programın, stdout'u bir tty (pty.spawn'a benzer) olarak gördüğü Python tarafından çağrılması için bir yol bulamadım, ancak girişi doğru olarak yankılandı (subprocess.call benzeri). Herhangi bir fikir?

cevap

3

Bir dosyaya stdout bağlı bir terminal oluşturuyorsunuz, böylece terminallerin yaptıkları normal eko-geri, ekran yerine dosyaya gönderiliyor.

Im spawn böyle doğrudan kullanılmak üzere tasarlanmıştır emin olun: pty kütüphane bir alt süreç oluşturmak için pty.fork() sunar ve Stdin/stdout için bir dosya tanıtıcı ile döner. Ancak bunu kullanmak için çok daha fazla koda ihtiyacınız olacak.

Seçenek 1: bir dosyaya kökenli komutun çıktısını gönderiyor umurumda tüm, o zaman yapabiliyorsa

iki kolay seçenekler heres, sen spawn ile yaptığınız şimdiki sorunu aşmak için Bu gibi görünecektir

çalıştırın::

start to stdout only 
hello 
complete to stdout only 
(i piton için boru ve here dosyaları tek gömlekleri adında tercih) 210

, girişin (merhaba yazdım) ve baskı ifadelerinin sonucunun ekrana gittiğini gösterir. Out.txt dosyasının içeriği şu şekilde olacaktır: Yalnızca yazdığınız şey budur.

Seçenek 2: diğer taraftan sen kökenli komut çıktısı etrafında piton çıktı içermesini dışarı dosyayı istiyorum, o zaman gibi biraz daha karmaşık bir şey, gerek:

python <(cat << EOF 
import sys 
import pty 
import os 
old_stdout = sys.stdout 
sys.stdout = myfdout = os.fdopen(4,"w") 
print 'start to out file only' 
myfdout.flush() 
pty.spawn(sys.argv[1:]) 
print 'complete to out file only' 
sys.stdout = old_stdout 
EOF 
) bash -c 'cat >&4' 4>out.txt 

hangi edecek dışarı dosya içerecek

hello 

ama:

0 sadece (yazdığınız her ne yani) çalıştırın terminale bu çıkışı

Arka plan: python pty kütüphanesi güçlüdür: onun python bağlı bir terminal aygıtı oluşturma stdout ve stdin. İdenin en çok kullandığını düşünmek, gerçek stdin/stdout'un etkilenmemesi için pty.fork() aramasını kullanacaktır.

Sizin durumunuzda, kabuğunuzda, python işleminin stdout'unu bir dosyaya yönlendirdiniz. Sonuçta oluşan pty, bu nedenle, stdout'un dosyaya eklenmiş olmasını sağlamıştır, böylece stdut'a geri eklenmiş stdin'in normal hareketi yeniden yönlendirilmiştir. Düzenli stdout (ekran) hala yerindeydi ancak yeni pty tarafından kullanılmıyordu.

Seçenek yukarıda 1 için temel fark Pty oluşturulan hala (gerçek terminali Stdout'a açık bir bağlantı vardır, böylece yankı çalıştığında için, pty.spawn çağrısı içinde bir yerde ortaya Stdout'a yeniden yönlendirme taşımaktır yazarken stdin)

Seçenek 2 için fark piton içine girince, keyfi bir dosya tanımlayıcı üzerinde ikinci bir kanal (yani dosya tanımlayıcı 4) oluşturmak ve stdout yerine bu kullanmaktır ve oluşturduğunuz zaman doğuştan işleminiz (yani, aynı süreç tanımlayıcısına sahip olduğunuz işlemin stdout'unu yeniden yönlendirin)

Bu farkların her ikisi de, pty.spawn'un stdout'unun değiştirilmesinden veya gerçek terminalden ayrılmasından kaynaklanan piti önler. Bu, stdin'in doğru bir şekilde çalışmasını sağlar.

pty kütüphane kullanımı ve daha fazla kontrol elde paketleri vardır ama bunların çoğu pty.fork() kullanmak (ve ilginçtir ben kimse aslında pty.spawn kullanır sığınak bulundu) bir örnek İşte

DÜZENLEMEYİ bulacaksınız Bu questionpty.fork()

için bazı iyi bağlantıları vardır

import sys 
import pty 
import os 
import select 
import time 
import tty 
import termios 

print 'start' 
try: 
    pid, fd = pty.fork() 
    print 'forked' 
except OSError as e: 
    print e 

if pid == pty.CHILD: 
    cmd = sys.argv[1] 
    args = sys.argv[1:] 
    print cmd, args 
    os.execvp(cmd,args) 
else: 
    tty.setraw(fd, termios.TCSANOW) 
    try: 
     child_file = os.fdopen(fd,'rw') 
     read_list = [sys.stdin, child_file] 
     while read_list: 
      ready = select.select(read_list, [], [], 0.1)[0] 
      if not ready and len(read_list) < 2: 
       break 
      elif not ready: 
       time.sleep(1) 
      else: 
       for file in ready: 
        try: 
         line = file.readline() 
        except IOError as e: 
         print "Ignoring: ", e 
         line = None 
        if not line: 
         read_list.remove(file) 
        else: 
         if file == sys.stdin: 
          os.write(fd,line) 
         else: 
          print "from child:", line 
    except KeyboardInterrupt: 
     pass 

EDIT: pty.fork() kullanma

GÜNCELLEME: kodunda bazı yorumlar vermeliydim pty.fork() örnek nasıl çalışır:

çevirmeni pty.fork() çağrısı yürütür

, processesing ikiye böler: Her iki görünen iki konu şimdi vardır Sadece pty.fork() aramasını yaptım.

Bir iş parçacığı, başlangıçta içinde bulunduğunuz (üst) ve bir tanesi yeni bir iş parçacığı (alt öğe).ebeveyn olarak

, pid ve fd çocuğun işlem kimliği ve teh çocuğun stdin ve stdout ünitesine bağlı bir dosya decriptor ayarlanır: ebeveyn de, size karşı yazılanların okuduğunuz fd doğru okunduğunda oğullar stdout; fd'a yazdığınızda childs'ın stdinine yazıyorsunuz. Şimdi, ebeveynin içinde, diğer iş parçacığıyla stdout/stdin üzerinden iletişim kurabiliyoruz.

Çocukta pid 0 olarak ayarlanmış ve fd ayarlanmamış. Eğer ebeveyn iş parçacığıyla konuşmak istiyorsak, ebeveynin bunu yapabileceği ve yapması gerektiğini bilerek stdin/stdout üzerinden okuyabilir ve yazabiliriz.

İki iş parçacığı, bu noktadan aynı kodu yürütecek, ancak ebeveyn veya çocuk iş parçacığının pid'daki değere göre olup olmadığını anlatabiliriz. Çocuğumuzda ve ebeveyneğimizde farklı şeyler yapmak istiyorsak, o zaman çocuğu bir kod yolunu ve ebeveynini farklı bir kod yoluna indiren bir koşullu ifadeye ihtiyacımız var. Bu hat ne Thats:

çocukta
if pid == pty.CHILD: 
    #child thread will execute this code 
    .... 
else 
    #parent thread will execute this code 
    ... 

, biz sadece yeni bir pty yeni komut yumurtlamaya istiyorum. os.execvp biz bu yöntemle ancak esasen onun ebeveyn de, gerçek Stdin bağlamanız gerekir aynı anda Yani fd`

. And the parent can write to the command via pty by writing to fd pty.spawn()'. This means the child stdin/stdout are now connected to the command you wanted via a pty. IMmportantly, any input or output from the command (or the pty for that matter) will be available to the parent thread by reading from olarak/stdout bir terminal olarak pty üzerinde daha fazla kontrole sahip olacak çünkü kullanılır çocuk stdin/stdout'a fd no'lu telefondan okuma ve yazma yoluyla. Artık ana kodun yaptığı şey (else bölüm). Gerçek stdin üzerinde ortaya çıkan herhangi bir veri fd'a yazılmıştır. fd'dan (ebeveyn tarafından) okunan veriler, stdout'a yazılır. Artık ebeveyn iş parçacığının yaptığı tek şey, gerçek stdin/stdout ve fd arasında proxy yapıyor. Girdiğin ve çıktı aldığın bir şeyle programlı bir şekilde yapmak istediysen, bunu yapacağın yer burası.

tty.setraw(fd, termios.TCSANOW) 

Bu eko-geri yaparak durdurmak için çocukta pty anlatmak için bir yoludur:

ebeveyn olur tek şey bu çağrıdır.

Bu başlangıçta yapıyorduk sorunu çözer: (girişinizi sürecine geçmeden önce yani) Normal eko-geri yerinde olduğundan - - - Yerel terminali sadece ana iş parçacığı bağlanır arasında stdout süreç yönlendirilebilir - çocuk süreç gibi görünüyor onun stdin

yerel eko-geri yapamaz çekildiği bildirildi - Eğer Terminal stdout ne yaparsak çocuk sürecinin ait stdin/stdout'ta herhangi bir etkisi yoktur Çok fazla açıklama var - eğer herhangi bir açıklık için düzenleme var mı?

+0

solution1 seçeneği ve option2 benim için çalışmaz çünkü dış python işlemi için (yani, diğer programları arayan/çoğaltan) stdout dosyası bir dosyaya/boruya yönlendirilir ve bu benim kontrolüm altında değildir. Bu şekilde başka biri tarafından çağrılır, sadece benim kontrolüm altında çalıştırılan python betiğine sahibim). Bu yüzden çıktıyı iç süreçlere yönlendirip seçemiyorum. (Ama evet, bu özgürlüğe sahip olsaydım, seçenek1 ve 2 kabul edilebilir olurdu) –

+0

Son örnek kodu çalıştırdım (** EDIT ** içinde) ve bu aslında benim sorunumu çözüyor gibi görünüyor. Ama hala nasıl çalıştığını kafamın etrafına sarmak için mücadele ediyorum :( –

+0

@ abhishek-kedia Ive bir açıklama ekledi – spacepickle