web-dev-qa-db-fra.com

Comment puis-je envoyer des commandes à des fenêtres de terminal spécifiques?


J'aimerais écrire un script pour ouvrir plusieurs programmes (serveurs) simultanément dans des terminaux distincts - peu importe lequel - et affecter différentes commandes à différents terminaux avec des commandes "atterrissant" à l'intérieur du bon terminal. Est-ce possible?
Peut-être, quelque chose comme ceci:

  1. terminal ouvert1
  2. ouvrez terminal2 // simultanément avec 1.
  3. commande1 // exécuter dans terminal1 sans ouvrir une nouvelle fenêtre de terminal
  4. commande2 // exécuter dans terminal2 sans ouvrir une nouvelle fenêtre de terminal
  5. ...

Puis-je en quelque sorte étiqueter les fenêtres de terminal pour que les commandes soient exécutées à l'intérieur du bon terminal?

J'aimerais aussi regarder tous les terminaux pendant que leurs programmes sont en cours d'exécution - mes programmes ont un argument pour imprimer la trace/le débogage sur le terminal. J'aimerais donc voir quels messages sont échangés entre eux.

REMARQUE: la sécurité des données échangées m'inquiète moins, car ce script devrait servir de "simulation". J'ai configuré chaque serveur pour qu'il s'exécute à partir d'un port alloué sur localhost.

13
Aliakbar Ahmadi

Puisque vous mentionnez que vous avez résolu le problème pour votre situation spécifique, vous trouverez ci-dessous une solution à usage général. Grâce à l'option --sync de xdotool, cela fonctionne de manière assez fiable dans les tests que j'ai effectués; Je pouvais "envoyer" des commandes à des fenêtres de terminal spécifiques et cela fonctionnait parfaitement sans exception.

Comment ça marche en pratique

La solution existe à partir d'un script, qui peut être exécuté avec deux options -set et -run:

  1. Pour définir up (ouvrir) un nombre arbitraire de fenêtres de terminaux, dans cet exemple 3:

    target_term -set 3
    

    Trois nouveaux terminaux s'ouvriront, leur identifiant de fenêtre est mémorisé dans un fichier caché:

    enter image description here

    Pour des raisons de clarté, j'ai minimisé la fenêtre du terminal à partir duquel j'ai exécuté la commande :)

  2. Maintenant que j'ai créé trois fenêtres, je peux envoyer des commandes à l’une d’elles avec la commande run (exemple):

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

    Comme indiqué ci-dessous, la commande a été exécutée dans le deuxième terminal:

    enter image description here

    Par la suite, je peux envoyer une commande au premier terminal:

     target_term -run 1 Sudo apt-get update
    

    en faisant Sudo apt-get update exécuté dans le terminal 1:

    enter image description here

    etc...

Comment mettre en place

  1. Le script a besoin des noms wmctrl et xdotool:

    Sudo apt-get install wmctrl xdotool
    
  2. Copiez le script ci-dessous dans un fichier vide, en le sauvegardant sous target_term (sans extension!) Dans ~/bin (créez le répertoire ~/bin si nécessaire.

  3. Rendez le script exécutable (n'oubliez pas) et déconnectez-vous/ouvrez ou exécutez:

    source ~/.profile
    
  4. Maintenant, configurez vos fenêtres de terminal, avec le nombre de fenêtres requises comme argument:

    target_term -set <number_of_windows>
    
  5. Vous pouvez maintenant "envoyer" des commandes à l’un ou l’autre de vos terminaux avec la commande suivante:

    target_term -run <terminal_number> <command_to_run>
    

Le scénario

#!/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)

Remarques

  • Le script est défini sur gnome-terminal, mais peut être utilisé pour tout terminal (ou tout autre programme) en modifiant le application dans la section head du script:

    #--- set your terminal below
    application = "gnome-terminal"
    #---
    
  • Les commandes ci-dessus peuvent (bien sûr) être exécutées à partir d'un script également, au cas où vous le souhaiteriez pour une simulation.
  • Le script attend que la fenêtre ciblée soit activée et que la commande soit terminée. La commande atterrit donc toujours dans la fenêtre du terminal de droite.
  • Inutile de dire que le script ne fonctionne qu'avec la configuration du terminal (Windows) appelée par la commande:

    target_term -set
    

    Les fenêtres du terminal seront alors "étiquetées" par le script, comme vous le mentionnez dans votre question.

  • Si vous démarrez une nouvelle session target_term, le fichier caché, créé par le script, sera simplement écrasé. Il n'est donc pas nécessaire de le supprimer.
14
Jacob Vlijm