Pertanyaan Bagaimana saya bisa memasukkan input awal ke dalam proses yang kemudian akan menjadi interaktif?


Saya ingin dapat menyuntikkan perintah awal ke peluncuran proses interaktif, sehingga saya bisa melakukan sesuatu seperti ini:

echo "initial command" | INSERT_MAGIC_HERE some_tool

tool> initial command 

[result of initial command] 

tool> [now I type an interactive command]

Apa yang tidak berhasil:

  • Hanya mem-pipe perintah awal tidak berfungsi, karena ini menyebabkan stdin tidak terhubung ke terminal

  • Menulis ke / dev / pts / [number] mengirim output ke terminal, bukan input ke proses seolah-olah dari terminal

Apa yang akan tetapi dengan kerugian:

  • Buatlah sebuah perintah yang membacakan seorang anak, tulis ke stdinnya dan kemudian majukan semuanya dari stdinnya sendiri. Kelemahan - hal kontrol terminal (seperti mode baris vs karakter) tidak akan berfungsi. Mungkin saya bisa melakukan sesuatu dengan proksi terminal pseudo?

  • Buatlah versi modifikasi dari xterm (saya meluncurkan satu untuk tugas ini juga) dengan opsi baris perintah untuk menyuntikkan perintah tambahan setelah menemukan string prompt yang diinginkan. Jelek.

  • Buat versi modifikasi dari alat yang saya coba jalankan sehingga ia menerima perintah awal pada baris perintah. Mematahkan instalasi standar.

(Alat yang menarik saat ini, kebetulan, adalah shell adb android - saya ingin membuka shell interaktif di telepon, menjalankan perintah secara otomatis, dan kemudian memiliki sesi interaktif)


32
2018-04-30 18:18


asal


Jawaban:


Anda tidak perlu menulis alat baru untuk meneruskan stdin - satu telah ditulis (cat):

(echo "initial command" && cat) | some_tool

Ini memang memiliki sisi negatif dari menghubungkan pipa ke some_tool, bukan terminal.


33
2018-05-02 00:55



Jawaban yang diterima sederhana dan sebagian besar baik.

Tetapi itu memiliki kerugian: program mendapatkan pipa sebagai inputnya, bukan terminal. Ini berarti pelengkapan otomatis tidak akan berfungsi. Dalam banyak kasus, ini juga menonaktifkan output yang cukup, dan saya telah mendengar beberapa program hanya menolak untuk bekerja jika stdin bukan terminal.

Program berikut ini memecahkan masalah. Ini menciptakan pseudoterminal, memunculkan program yang terhubung ke pseudoterminal ini. Ini pertama kali memberi makan masukan ekstra dilewatkan melalui commandline, dan kemudian memberi makan masukan yang diberikan oleh pengguna melalui stdin.

Sebagai contoh, ptypipe "import this" python3 membuat Python mengeksekusi "import this" terlebih dahulu, dan kemudian menjatuhkan Anda ke command prompt interaktif, dengan penyelesaian pekerjaan dan hal-hal lain.

Juga, ptypipe "date" bash menjalankan Bash, yang mengeksekusi date lalu memberikan cangkang kepada Anda. Sekali lagi, dengan penyelesaian kerja, prompt berwarna dan sebagainya.

#!/usr/bin/env python3

import sys
import os
import pty
import tty
import select
import subprocess

STDIN_FILENO = 0
STDOUT_FILENO = 1
STDERR_FILENO = 2

def _writen(fd, data):
    while data:
        n = os.write(fd, data)
        data = data[n:]

def main_loop(master_fd, extra_input):
    fds = [master_fd, STDIN_FILENO]

    _writen(master_fd, extra_input)

    while True:
        rfds, _, _ = select.select(fds, [], [])
        if master_fd in rfds:
            data = os.read(master_fd, 1024)
            if not data:
                fds.remove(master_fd)
            else:
                os.write(STDOUT_FILENO, data)
        if STDIN_FILENO in rfds:
            data = os.read(STDIN_FILENO, 1024)
            if not data:
                fds.remove(STDIN_FILENO)
            else:
                _writen(master_fd, data)

def main():
    extra_input = sys.argv[1]
    interactive_command = sys.argv[2]

    if hasattr(os, "fsencode"):
        # convert them back to bytes
        # http://bugs.python.org/issue8776
        interactive_command = os.fsencode(interactive_command)
        extra_input = os.fsencode(extra_input)

    # add implicit newline
    if extra_input and extra_input[-1] != b'\n':
        extra_input += b'\n'

    # replace LF with CR (shells like CR for some reason)
    extra_input = extra_input.replace(b'\n', b'\r')

    pid, master_fd = pty.fork()

    if pid == 0:
        os.execlp("sh", "/bin/sh", "-c", interactive_command)

    try:
        mode = tty.tcgetattr(STDIN_FILENO)
        tty.setraw(STDIN_FILENO)
        restore = True
    except tty.error:    # This is the same as termios.error
        restore = False

    try:
        main_loop(master_fd, extra_input)
    except OSError:
        if restore:
            tty.tcsetattr(0, tty.TCSAFLUSH, mode)

    os.close(master_fd)
    return os.waitpid(pid, 0)[1]

if __name__ == "__main__":
    main()

(Catatan: Saya khawatir solusi ini mengandung kemungkinan deadlock. Anda mungkin ingin memberi makan tambahan_input dalam potongan kecil untuk menghindarinya)


3
2018-06-03 23:14



Ini mudah dilakukan dengan program "mengharapkan" yang dimaksudkan untuk membiarkan Anda menulis skrip untuk berinteraksi dengan program.

Saya menguji ini dengan menulis sebuah skrip mengharapkan bc.exp untuk meluncurkan kalkulator "bc" dan mengirimkannya perintah "obase = 16" untuk memasukkannya ke dalam mode keluaran heksadesimal, dan kemudian menyerahkan kendali kepada saya.

Skrip (dalam file bernama bc.exp) adalah

spawn bc
send "obase=16\n"
interact {
 \003 exit
}

Satu menjalankannya dengan

expect bc.exp

1
2017-07-21 02:48



Mungkin Anda bisa menggunakannya dokumen di sini untuk meneruskan masukan Anda abd. E. g. seperti ini (menggunakan bc untuk melakukan perhitungan sederhana sebagai contoh).

[axe@gromp ~]$ bc <<END
> 3 + 4
> END
7

Itu bc sesi tetap terbuka setelah itu, jadi apa yang disediakan antara penanda awal dan akhir (antara "<< END" dan "END") akan diteruskan ke perintah.


-2
2018-04-30 18:44