How can I send commands to specific terminal windows?

15,302

Since you mention you solved the problem for your specific situation, below a solution for general purpose. Thanks to xdotool's --sync option, it works pretty reliable in the tests I ran; I could "send" commands to specific terminal windows and it ran perfectly without an exception.

How it works in practice

The solution exists from a script, which can be run with two options -set and -run:

  1. To set up (open) an arbitrary number of terminal windows, in this example 3:

    target_term -set 3
    

    Three new terminals will open up, their window id is remembered in a hidden file:

    enter image description here

    For clarity reasons I minimized the terminal window I ran the command from :)

  2. Now that I created three windows, I can send commands to either one of them with the run command (e.g.):

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

    As shown below, the command ran in the second terminal:

    enter image description here

    Subsequently, I can send a command to the first terminal:

     target_term -run 1 sudo apt-get update
    

    making sudo apt-get update run in terminal 1:

    enter image description here

    and so on...

How to set up

  1. The script needs both wmctrl and xdotool:

    sudo apt-get install wmctrl xdotool
    
  2. Copy the script below into an empty file, safe it as target_term (no extension!) in ~/bin (create the directory ~/bin if necessary.

  3. Make the script executable (don't forget) and either log out/in or run:

    source ~/.profile
    
  4. Now setup your terminal windows, with the number of required windows as an argument:

    target_term -set <number_of_windows>
    
  5. Now you can "send" commands to either one of your terminals with the command:

    target_term -run <terminal_number> <command_to_run>
    

The 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)

Notes

  • The script is set for gnome-terminal, but can be used for any terminal (or other program as well) by changing the application in the head section of the script:

    #--- set your terminal below
    application = "gnome-terminal"
    #---
    
  • The commands above can (of course) be run from a script as well in case you'd lile to use it for some kind of a simulation.
  • The script waits until both the targeted window has focus and the command is done typing, so the command will always land in the right terminal window.
  • No need to say that the script only works with the terminal setup (windows) that was called by the command:

    target_term -set
    

    The terminal windows will then be "labelled" by the script, like you mention in your question.

  • In case you start a new target_term session, the hidden file, created by the script will simply be overwritten, so there is no need to remove it otherwise.
Share:
15,302

Related videos on Youtube

Aliakbar Ahmadi
Author by

Aliakbar Ahmadi

My Bachelor's Thesis investigated the implementation of the Selfish Mining Algorithm proposed by Eyal and Sirer (2013) and focused on the early detection of an occuring attack by the means of statistical analysis. I studied Mechanical Engineering at the University of Stuttgart and obtained a Masters degree in Mechanical Engineering and Management from the Technical University of Munich. As a visiting scholar at the Harvard Medical School in Boston, I looked at the head and eye behaviour of visually impaired and elderly drivers and developed a genetic algorithm for automatic and accurate detection of large eye movements. Currently, I am looking for interesting and challenging entry level positions in the field of network security at the intersection of big data analysis and machine learning. Bitocin.Stackexchange and its community decisively motivated me to find my path of interest not only in blockchain tech but in IT in general. (Stuttgart, 2021)

Updated on September 18, 2022

Comments

  • Aliakbar Ahmadi
    Aliakbar Ahmadi over 1 year


    I'd like to write a script for opening multiple programs (servers) simultanously in separate terminals - doesn't matter which one - and assign different commands to different terminals with commands "landing" inside the correct terminal. Is this possible?
    Maybe, something like this:

    1. open terminal1
    2. open terminal2 //simultanously with 1.
    3. command1 //execute in terminal1 without opening a new terminal window
    4. command2 //execute in terminal2 without opening a new terminal window
    5. ...

    Can I somehow label terminal windows so commands are executed inside correct terminal?

    I'd also like to watch all terminals while their programs are running - my programs have an argument for printing trace/debug to terminal. So I'd like to see what messages are exchanged between them.

    NOTE: I'm less concerned about security of exchanged data since this script should serve as a "simulation". I've configured each server to run from an allocated port on localhost.

    • heemayl
      heemayl almost 9 years
      Check pssh ....
    • Jacob Vlijm
      Jacob Vlijm almost 9 years
      How accurate should the timing be; is a marge of let's say 2 seconds (per terminal) appropriate?
    • Aliakbar Ahmadi
      Aliakbar Ahmadi almost 9 years
      @JacobVlijm: it's more important to me to assign commands correctly to according terminal "window"
    • Jacob Vlijm
      Jacob Vlijm almost 9 years
      Can be done, especially when it is about simulation, will post back :)
    • Aliakbar Ahmadi
      Aliakbar Ahmadi almost 9 years
      @JacobVlijm: I'm going to send commands in for loops inside script with a random sleep time in between.
    • Jacob Vlijm
      Jacob Vlijm almost 9 years
      Last question, in a single command, or do you need to be able to run additional commands afterwards?
    • Aliakbar Ahmadi
      Aliakbar Ahmadi almost 9 years
      Yes exactly this is my aim...i'm running multiple instances of bitcoind and want to let each instance mine 1 block after a random sleep time over and over again
    • Jacob Vlijm
      Jacob Vlijm almost 9 years
      Very interesting! A bit more complicated than I assumed, will continue tomorrow late . (if not someone posted a brilliant solution before :) )
    • Aliakbar Ahmadi
      Aliakbar Ahmadi almost 9 years
      @JacomVlijm: actually my question is incidently solved: for sending a command to its correct instance each command must be prefixed with the datadir that instance starts upon! But for my luck this is implemented in bitcoin but i'll just leave the question unanswered .. perhaps someone comes up with a more general idea for any program!? :) But thanks though!
    • Jacob Vlijm
      Jacob Vlijm almost 9 years
      I posted my answer. could you test it?
  • pompalini
    pompalini almost 5 years
    Nice one, thanks! Should also be noted that python 3.x is also a requirement for this script to work.