web-dev-qa-db-fra.com

Comment imprimer du texte dans le terminal comme s'il était saisi?

J'ai un echo simple imprimé que j'ai ajouté à mon .bashrc:

echo "$(tput setaf 2)Wake up....."
sleep 2s
reset
sleep 2s
echo "$(tput setaf 2)Wake up....."
sleep 2s
reset
echo "$(tput setaf 2)Wake up neo....."
sleep 2s
echo "$(tput setaf 2)The Matrix has you......"
sleep 2s
reset
echo "$(tput setaf 2)Follow the white rabbit......"
sleep 2s
reset
cmatrix

Cela affiche un message sur le terminal, mais je veux qu'il ressemble à une saisie, avec un délai constant entre les caractères.

26
SimplySimplified

Cela ne fonctionne pas avec Wayland; Si vous utilisez Ubuntu 17.10 et que vous n'avez pas utilisé Xorg lors de la connexion, cette solution ne vous convient pas.

Vous pouvez utiliser xdotool Install xdotool pour cela. Si le délai entre les frappes doit être cohérent , c'est aussi simple que cela:

xdotool type --delay 100 something

Ceci tape something avec un délai de 100 millisecondes entre chaque frappe.


Si le délai entre les frappes doit être aléatoire , par exemple de 100 à 300 millisecondes, les choses se compliquent un peu:

$ text="some text"
  for ((i=0;i<${#text};i++));
  do
    if [[ "${text:i:1}" == " " ]];
    then
      echo -n "key space";
    else
      echo -n "key ${text:i:1}";
    fi;
  [[ $i < $((${#text}-1)) ]] && echo -n " sleep 0.$(((RANDOM%3)+1)) ";
  done | xdotool -

Cette boucle for parcourt chaque lettre de la chaîne enregistrée dans la variable text et affiche key <letter> ou key space dans le cas d’un espace suivi de sleep 0. et d’un nombre aléatoire compris entre 1 et 3 (xdotool 's sleep interprète le nombre en secondes). La totalité de la sortie de la boucle est ensuite redirigée vers xdotool, qui imprime les lettres avec le délai aléatoire entre les deux. Si vous souhaitez modifier le délai, il suffit de modifier la partie (RANDOM%x)+y, y étant la limite inférieure et x-1+y la limite supérieure - pour 0,2 à 0,5 seconde, il s'agira de (RANDOM%4)+2.

Notez que cette approche ne n'imprime pas le texte, mais le tape exactement comme le ferait l'utilisateur, en synthétisant des pressions uniques. En conséquence, le texte est tapé dans la fenêtre actuellement active. Si vous modifiez la partie active du texte, celle-ci sera saisie dans la nouvelle fenêtre, ce qui peut être différent de ce que vous voulez. Dans les deux cas, jetez un coup d’œil aux autres réponses, toutes brillantes!

28
dessert

J'ai essayé xdotool après avoir lu la réponse de @ dessert mais je n'ai pas réussi à le faire fonctionner pour une raison quelconque. Alors je suis venu avec ceci:

while read line
do
    grep -o . <<<$line | while read a
    do
        sleep 0.1
        echo -n "${a:- }"
    done
    echo
done

Introduisez votre texte dans le code ci-dessus et il sera imprimé comme dactylographié. Vous pouvez également ajouter un caractère aléatoire en remplaçant sleep 0.1 par sleep 0.$((RANDOM%3)).

Version étendue avec de fausses fautes de frappe

Cette version introduira une fausse typo de temps en temps et la corrigera:

while read line
do
    # split single characters into lines
    grep -o . <<<$line | while read a
    do
        # short random delay between keystrokes
        sleep 0.$((RANDOM%3))
        # make fake typo every 30th keystroke
        if [[ $((RANDOM%30)) == 1 ]]
        then
            # print random character between a-z
            printf "\\$(printf %o "$((RANDOM%26+97))")"
            # wait a bit and delete it again
            sleep 0.5; echo -ne '\b'; sleep 0.2
        fi
        # output a space, or $a if it is not null
        echo -n "${a:- }"
    done
    echo
done
24
Sebastian Stark

Vous mentionnez un délai cohérent entre les caractères, mais si vous voulez vraiment que le texte soit saisi, le timing ne sera pas parfaitement cohérent. Pour ce faire, vous pouvez enregistrer votre propre frappe avec la commande scriptet la lire avec scriptreplayname__:

$ script -t -c "sed d" script.out 2> script.timing
Script started, file is script.out
Wake up ...
Wake up ...
Wake up Neo ...
Script done, file is script.out
$ 
$ scriptreplay script.timing script.out
Wake up ...
Wake up ...
Wake up Neo ...

$ 

L'enregistrement est arrêté en appuyant sur CTRL-D.

Passer le paramètre -t à scriptlui indique également de générer des informations de minutage, que j'ai redirigées vers le fichier script.timing. J'ai transmis sed d en tant que commande à scriptcar il s'agit simplement d'un moyen d'absorber les entrées (et d'enregistrer les frappes au clavier) sans effets secondaires.

Si vous souhaitez également effectuer toutes les opérations tputname __/resetname__, vous pouvez également créer un enregistrement scriptpour chacune de vos lignes et les lire, entrelacées avec les commandes tputname__/resetname__.

18
Digital Trauma

Une autre possibilité consiste à utiliser Demo Magic , ou plus précisément la fonction d'impression de cette collection de scripts, ce qui revient essentiellement à

#!/bin/bash

. ./demo-magic.sh -w2

p "this will look as if typed"

Sous le capot, cela utilise pv , que vous pouvez bien sûr aussi utiliser pour obtenir directement l'effet souhaité, la forme de base se présente comme suit:

echo "this will look as if typed" | pv -qL 20
11
godfatherofpolka

Conformément à mon pseudo, je peux proposer une autre solution:

echo "something" | 
    Perl \
        -MTime::HiRes=usleep \
        -F'' \
        -e 'BEGIN {$|=1} for (@F) { print; usleep(100_000+Rand(200_000)) }'

Ça a l'air bizarre, n'est-ce pas?

  • -MTime::HiRes=usleep importe la fonction usleep (veille microseconde) à partir du module Time::HiRes car le nom sleep habituel n'accepte que des secondes entières.
  • -F'' divise l'entrée donnée en caractères (le délimiteur étant vide '') et place les caractères dans le tableau @F.
  • BEGIN {$|=1} désactive la mise en mémoire tampon de sorte que chaque caractère soit immédiatement imprimé.
  • for (@F) { print; usleep(100_000+Rand(200_000)) } itère juste sur les caractères
  • mettre les traits de soulignement en chiffres est un moyen courant d’utiliser une sorte de milliers de séparateurs en Perl. Ils sont simplement ignorés par Perl, nous pouvons donc par exemple écrivez 1_000 (== 1000) ou même 1_0_00 si nous considérons que cela est plus facile à lire.
  • Rand() renvoie un nombre aléatoire compris entre 0 et l'argument donné. Par conséquent, la durée du sommeil est comprise entre 100 000 et 299 999 microsecondes (0,1 à 0,3 seconde).
8
PerlDuck

Un autre outil susceptible de fonctionner, qui ne dépend pas de x11 ni de quoi que ce soit, est asciicinema . Il enregistre tout ce que vous faites dans votre terminal et vous permet de rejouer cela comme s'il s'agissait d'une capture d'écran, mais uniquement à partir de là! Cependant, vous devrez peut-être désactiver temporairement votre invite pour qu'elle soit purement visuelle. Comme d'autres l'ont fait remarquer, l'ajout d'un délai constant ne semblera pas naturel, et le taper vous-même pourrait être l'un des aspects les plus naturels que vous puissiez obtenir.

Après avoir enregistré le texte, vous pouvez faire quelque chose comme:

$ asciinema play [your recording].cast; cmatrix
6
rien333

Je suis surpris que personne n'en ait encore parlé, mais vous pouvez le faire avec des outils de stock et une boucle:

typeit() {
    local IFS=''
    while read -n1 c; do
        echo -n "$c"
        sleep .1
    done <<< "$1"
}

Il passe en boucle sur les caractères saisis, caractère par caractère, et les affiche avec un délai après chacun. Le seul problème est que vous devez définir votre IFS sur une chaîne vide afin que bash n'essaye pas de scinder vos espaces.

Cette solution est extrêmement simple. Vous devez donc ajouter des retards variables entre les caractères, les fautes de frappe et tout ce qui est super facile.

EDIT (merci, @dessert): Si vous voulez une interface légèrement plus naturelle, vous pouvez plutôt faire

typeit() {
    local IFS=''
    while read -n1 c; do
        echo -n "$c"
        sleep .1
    done <<< "$@"
}

Cela vous permettrait d'appeler la fonction en tant que typeit foo bar plutôt que typeit 'foo bar'. Sachez que sans les guillemets, les arguments sont soumis au fractionnement de Word de bash. Ainsi, par exemple, typeit foo<space><space>bar affichera foo<space>bar. Pour conserver les espaces, utilisez des guillemets.

6
whereswalden

Premièrement, "regardez comme si c'était tapé, avec un décalage constant entre les caractères ..." est un peu contradictoire, comme d'autres l'ont souligné. Quelque chose en cours de dactylographie n'a pas un délai cohérent. Lorsque vous voyez quelque chose produit avec un délai incohérent, vous aurez des frissons. "Qu'est-ce qui a été pris sur mon ordinateur !!! ??!?"

En tous cas...

Je dois faire un appel à expect, qui devrait être disponible sur la plupart des distributions Linux. Old School, je sais, mais si elle est installée, ce ne pourrait être plus simple:

echo 'set send_human {.1 .3 1 .05 2}; send -h "The Matrix has you......\n"' | expect -f /dev/stdin

De la page de manuel:

Le drapeau -h force la sortie à être envoyée (un peu) comme un humain en train de taper. Des délais ressemblant à des humains apparaissent entre les personnages. (L'algorithme est basé sur une distribution de Weibull, avec des modifications adaptées à cette application particulière.) Cette sortie est contrôlée par la valeur de la variable "send_human" ...

Voir https://www.tcl.tk/man/expect5.31/expect.1.html

5
Mike S