Come posso inviare comandi a finestre di terminali specifiche?

12


Mi piacerebbe scrivere uno script per l'apertura simultanea di più programmi (server) in terminali separati - non importa quale - e assegnare comandi diversi a terminali diversi con i comandi "atterraggio" all'interno del terminale corretto. È possibile?
Forse, qualcosa del genere:

  1. apri terminal1
  2. apri terminal2 // simultaneamente con 1.
  3. comando1 // esegue in terminal1 senza aprire una nuova finestra di terminale
  4. comando2 // esegue in terminal2 senza aprire una nuova finestra di terminale
  5. ...

Posso in qualche modo etichettare le finestre dei terminali in modo che i comandi vengano eseguiti all'interno del terminale corretto?

Mi piacerebbe anche guardare tutti i terminali mentre i loro programmi sono in esecuzione - i miei programmi hanno un argomento per stampare traccia / debug sul terminale. Quindi mi piacerebbe vedere quali messaggi vengono scambiati tra loro.

NOTA: Sono meno preoccupato per la sicurezza dei dati scambiati poiché questo script dovrebbe servire come una "simulazione". Ho configurato ciascun server per l'esecuzione da una porta allocata su localhost.

    
posta Aliakbar Ahmadi 27.06.2015 - 19:12
fonte

1 risposta

13

Dato che hai menzionato hai risolto il problema per la tua situazione specifica, sotto una soluzione per scopi generali. Grazie all'opzione xdotool di --sync , funziona in modo abbastanza affidabile nei test che ho eseguito; Potrei "inviare" comandi a finestre terminali specifiche e funzionò perfettamente senza eccezioni.

Come funziona in pratica

La soluzione esiste da uno script, che può essere eseguito con due opzioni -set e -run :

  1. Per impostare su (apri) un numero arbitrario di finestre di terminale, in questo esempio 3:

    target_term -set 3
    

    Si apriranno tre nuovi terminali, il loro id della finestra viene ricordato in un file nascosto:

    Per motivi di chiarezza ho ridotto al minimo la finestra del terminale da cui ho eseguito il comando:)

  2. Ora che ho creato tre finestre, posso inviare comandi a uno di essi con il comando esegui (ad es.):

    target_term -run 2 echo "Monkey eats banana since it ran out of peanuts"
    

    Come mostrato sotto, il comando ha funzionato nel secondo terminale:

    Successivamente, posso inviare un comando al primo terminale:

     target_term -run 1 sudo apt-get update
    

    facendo sudo apt-get update eseguito nel terminale 1:

    e così via ...

Come impostare

  1. Lo script richiede sia wmctrl che xdotool :

    sudo apt-get install wmctrl xdotool
    
  2. Copia lo script sottostante in un file vuoto, sicuro come target_term (senza estensione!) in ~/bin (crea la directory ~/bin se necessario.

  3. Rendi lo script eseguibile (non dimenticare) e fai il logout / in o esegui:

    source ~/.profile
    
  4. Ora imposta le finestre del terminale, con il numero di finestre richieste come argomento:

    target_term -set <number_of_windows>
    
  5. Ora puoi "inviare" i comandi a uno dei tuoi terminali con il comando:

    target_term -run <terminal_number> <command_to_run>
    

Lo script

#!/usr/bin/env python3
import subprocess
import os
import sys
import time
#--- set your terminal below
application = "gnome-terminal"
#---

option = sys.argv[1]
data = os.environ["HOME"]+"/.term_list"

def current_windows():
    w_list = subprocess.check_output(["wmctrl", "-lp"]).decode("utf-8")
    w_lines = [l for l in w_list.splitlines()]
    try:
        pid = subprocess.check_output(["pgrep", application]).decode("utf-8").strip()
        return [l for l in w_lines if str(pid) in l]
    except subprocess.CalledProcessError:
        return []

def arr_windows(n):
    w_count1 = current_windows()
    for requested in range(n):
        subprocess.Popen([application])
    called = []
    while len(called) < n:
        time.sleep(1)
        w_count2 = current_windows()
        add = [w for w in w_count2 if not w in w_count1]
        [called.append(w.split()[0]) for w in add if not w in called]
        w_count1 = w_count2

    return called

def run_intterm(w, command):
    subprocess.call(["xdotool", "windowfocus", "--sync", w])
    subprocess.call(["xdotool", "type", command+"\n"]) 

if option == "-set":
    open(data, "w").write("")
    n = int(sys.argv[2])
    new = arr_windows(n)
    for w in new:
        open(data, "a").write(w+"\n")
elif option == "-run":
    t_term = open(data).read().splitlines()[int(sys.argv[2])-1]
    command = (" ").join(sys.argv[3:])
    run_intterm(t_term, command)

Note

  • Lo script è impostato per gnome-terminal , ma può essere utilizzato per qualsiasi terminale (o anche un altro programma) modificando application nella sezione head dello script:

    #--- set your terminal below
    application = "gnome-terminal"
    #---
    
  • I comandi di cui sopra possono (ovviamente) essere eseguiti da uno script anche nel caso in cui tu voglia utilizzarlo per qualche tipo di simulazione.
  • Lo script attende fino a quando sia la finestra di destinazione sia stata messa a fuoco e il comando ha terminato di digitare, quindi il comando verrà sempre visualizzato nella finestra del terminale di destra.
  • Non c'è bisogno di dire che lo script funziona solo con l'installazione del terminale (windows) che è stata chiamata dal comando:

    target_term -set
    

    Le finestre del terminale saranno quindi "etichettate" dallo script, come menzionato nella tua domanda.

  • Se inizi una nuova sessione target_term , il file nascosto, creato dallo script verrà semplicemente sovrascritto, quindi non è necessario rimuoverlo altrimenti.
risposta data Jacob Vlijm 28.06.2015 - 22:38
fonte

Leggi altre domande sui tag