0

TL;DR How can I use python to ``simulate" typewriting commands in user's terminal? (something like the example below where I just really type the first command)

Exemple of scripted typewriting

I am using asciinema for displaying demos of terminal session. But instead of recording my own keystrokes (which unfortunately are not constant speed, prone to mistakes,...), I would like to script them, so the goal is the same as https://stackoverflow.com/a/63080929/5913047. But most tools (at least the ones in the answer of https://stackoverflow.com/a/63080929/5913047) are not maintained, so I was wondering if I could come up with a python script to do it.

Problem, I have no idea how to simulate typewriting in the user's terminal, especially using the syntax coloring and prompt of the user's terminal. I am pretty sure it is possible, since it is done in asciiscript (unfortunately this tool is buggy, in a language I do not know and not maintained). So is it possible to write in user's terminal, using user's syntax coloring and prompt, with python?

I imagine it has something to do with subprocess and pipes, but I have a hard time finding the information I need, and I am not too familiar with those.

Pierre Marchand
  • 619
  • 1
  • 7
  • 10
  • Do you need a typewriter like effect inside terminal using python? – Tsubasa Aug 17 '20 at 08:43
  • Do you need it to run commands in the terminal or just simulate the typing and the results? – The Otterlord Aug 17 '20 at 09:42
  • @TheOtterlord Ideally both, but if I can simulate the typing, I can just call the command via subprocess and display the output when the typing is done I think. – Pierre Marchand Aug 17 '20 at 09:45
  • @Ava Yes, using the user-s prompt and coloring. – Pierre Marchand Aug 17 '20 at 09:50
  • Does [tag:pexpect] work for you? – sexpect - Expect for Shells Aug 18 '20 at 10:14
  • @sexpect--Expect_for_Shells TBH I quickly looked at it but did not really understand what I could do with it. I guess it could solve my problem maybe. – Pierre Marchand Aug 18 '20 at 11:11
  • @sexpect--Expect_for_Shells It does look very nice, I managed to open a shell and send command. I just need to reproduce the typewriter effect now. – Pierre Marchand Aug 18 '20 at 11:26
  • @PierreMarchand - Just send slowly. :) – sexpect - Expect for Shells Aug 18 '20 at 11:28
  • @sexpect--Expect_for_Shells Could you do a small example so that I can accept your answer? It is not clear to me how to have the live output of the child and sending slowly characters of a command ^^' but I am just discovering Expect/pexpect. – Pierre Marchand Aug 18 '20 at 11:38
  • @PierreMarchand do you want the whole process to be automated? or you just want python to input some initial commands for you and then you'll take over control and manually interact with the shell? – pynexj Aug 18 '20 at 12:23
  • @pynexj Ideally, I want my program to read a file with a list of commands, then to typewrite them, and execute them one after the other in my terminal. I do not want to interact with it. – Pierre Marchand Aug 18 '20 at 12:25
  • that's quite easy with pexpect. do you really want to send each command char by char **slowly** like a real man typing in the terminal? – pynexj Aug 18 '20 at 13:08
  • to get started, you can take a look at this example: https://stackoverflow.com/a/46015727/900078 – pynexj Aug 18 '20 at 13:11
  • @pynexj I tried what is suggested in your link, but I want to typewrite in the user's command line, while you fixed the prompt to be `bash-[.0-9]+[$#] $` for example. It is "faking' a command line – Pierre Marchand Aug 18 '20 at 13:18
  • you can use whatever a prompt. just update the regex to match it. – pynexj Aug 18 '20 at 13:20
  • Yes, but my point is that I do not want to fix the prompt, I just want it to be the user's prompt. By the way, trying your code from your link, I get an error: `TypeError: write() argument must be str, not bytes` – Pierre Marchand Aug 18 '20 at 13:23
  • And I do not think that the coloring of what is being typed will match the coloring of the user's terminal (like in the example I gave in my question) – Pierre Marchand Aug 18 '20 at 13:24
  • the type error must be python3 related. – pynexj Aug 18 '20 at 13:24
  • still not clear to me what's your real requirement. – pynexj Aug 18 '20 at 13:25
  • I want something like in the gif from my question. Typing automatically commands in the user's terminal to have his syntax coloring and prompt. – Pierre Marchand Aug 18 '20 at 13:27
  • pexpect does not care about if the spawned process is colorful or not. for example if you send `ls --color=auto` (e.g. on Linux) then the result will be colorful. – pynexj Aug 18 '20 at 15:30
  • I also want the input to be colorized (like the strings and the command names in my example). – Pierre Marchand Aug 18 '20 at 15:39

1 Answers1

0

You can use a simple function that loops through each character to get a typewriter effect. The time.sleep method can slow down the text by setting a delay between each character print.

import time

def typewriter(text, timeout=0.1):
    for c in text:
        print(c, end="")
        time.sleep(timeout)
    print()

typewriter("Hello, world!")

You can combine this with the colorama Python module for the colouring. Then, as you said, use subprocess to run the command and print out the response.

# Execute the actual command and get the response text
response = subprocess.run(["echo", "\"Hello, world!\""], capture_output=True).stdout.decode()

# Print the response in a typewriter style.
typewriter(text)

# Or print it normally
print(text)

# Or print it line by line with a delay
for line in text.split("\n"):
    print(line)
    time.sleep(0.05)

Here is a more complete example.

import time, subprocess
from colorama import *

init()

def typewriter(cmd, timeout=0.1):
    text = ""
    for arg in cmd:
        text += arg + " "
    for c in text:
        print(c, end="")
        time.sleep(timeout)
    print()

def prompt():
    print(Fore.GREEN+"user@computer:"+Fore.BLUE+"~ $ "+Style.RESET_ALL, end="")

def command(cmd):
    prompt()
    typewriter(cmd)
    response = subprocess.run(cmd, capture_output=True).stdout.decode()
    print(response)

command(["echo", "Hello, world!"])

Please note that the colorama Python module may not work as expected on some devices. The colorama module does have documentation that I recommend visiting if you do run into any problems. I was testing this on a Linux machine in the terminal and everything worked fine.

The Otterlord
  • 1,680
  • 10
  • 20
  • The issue is that the displayed colors and the prompt are fixed in your answer, while I would like the colors and the prompt to be the one of the terminal used to run the python script. – Pierre Marchand Aug 17 '20 at 11:20