Come stampare il testo nel terminale come se fosse stato digitato?


27

Ho una semplice echostampa che ho aggiunto al mio .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

Questo stampa un messaggio sul terminale, ma voglio che sembri essere digitato, con un ritardo costante tra i caratteri.


1
Per essere realistici, penso che dovresti avere errori di battitura casuali inseriti con uno o più spazi posteriori per correggere. Ad esempio durante la digitazione di questo commento ho dovuto fare da back space su "through" per correggerlo su "buttato". Rende una domanda più difficile a cui rispondere, ma la rende più realistica. Se i personaggi si ripetono a un ritmo costante di 30 CPS o 60 CPS, sembra meno umano. Alcuni tasti vengono digitati più velocemente insieme, mentre altre combinazioni di tasti appaiono più lente. È necessaria una sorta di corrispondenza del modello delle combinazioni di tasti per la velocità.
WinEunuuchs2Unix

2
@ WinEunuuchs2Unix Non penso che sia ancora semplicemente semplificato. ;)
dessert

1
Vedi la mia risposta qui
In pausa fino a nuovo avviso.

Risposte:


28

Questo non funziona con Wayland; se stai usando Ubuntu 17.10 e non sei passato a usare Xorg al login, questa soluzione non fa per te.

Puoi usarlo xdotool Installa xdotoolper quello. Se il ritardo tra i tasti premuti è coerente , è semplice:

xdotool type --delay 100 something

Questo tipo somethingcon un ritardo di 100millisecondi tra ogni sequenza di tasti.


Se il ritardo tra i tasti premuti dovrebbe essere casuale , diciamo da 100 a 300 millisecondi, le cose diventano un po 'più complicate:

$ 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 -

Questo forciclo passa attraverso ogni singola lettera della stringa salvata in variabile text, stampando key <letter>o key spacenel caso di uno spazio seguito da sleep 0.e un numero casuale compreso tra 1 e 3 ( xdotool's sleepinterpreta il numero come secondi). Viene quindi reindirizzato l'intero output del loop xdotool, che stampa le lettere con il ritardo casuale tra. Se si desidera modificare il ritardo, è sufficiente cambiare la parte, essendo il limite inferiore e superiore - per 0,2 a 0,5 secondi sarebbe .(RANDOM%x)+yyx-1+y(RANDOM%4)+2

Si noti che questo approccio non stampa il testo, ma piuttosto lo digita esattamente come farebbe l'utente, sintetizzando i singoli tasti premuti. Di conseguenza il testo viene digitato nella finestra attualmente focalizzata; se si modifica la parte attiva del testo verrà digitata nella finestra appena focalizzata, che può essere o meno ciò che si desidera. In entrambi i casi, dai un'occhiata alle altre risposte qui, tutte brillanti!


24

Ho provato xdotool dopo aver letto la risposta di @ dessert ma non sono riuscito a farlo funzionare per qualche motivo. Quindi ho pensato a questo:

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

Inserisci il tuo testo nel codice sopra e verrà stampato come digitato. Puoi anche aggiungere casualità sostituendo sleep 0.1con sleep 0.$((RANDOM%3)).

Versione estesa con errori di battitura falsi

Questa versione introdurrà un falso errore ogni tanto e lo correggerà:

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

Penso che farei invece questo: while IFS= read -r line; do for (( i = 0; i < ${#line}; i++ )); do sleep 0.1; printf "%s" "${line:i:1}"; done; echo; done(sostituire ;con nuove righe e buona rientranza se necessario). L' IFS= read -re printf "%s"assicurarsi che gli spazi bianchi e caratteri speciali non sono trattati in modo diverso. E grepsu ogni riga per dividere in caratteri non è necessario - è sufficiente solo un forciclo su ogni carattere nella riga.
Trauma digitale

18

Menzionate un ritardo costante tra i personaggi, ma se volete davvero che sembri essere digitato, il tempismo non sarà perfettamente coerente. Per ottenere ciò è possibile registrare la propria digitazione con il scriptcomando e riprodurla con scriptreplay:

$ 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 ...

$ 

La registrazione viene interrotta premendo CTRL-D.

Passando il -tparametro per scriptindicare che genera anche informazioni di temporizzazione, che ho reindirizzato al script.timingfile. Ho passato sed dil comando in scriptquanto questo è semplicemente un modo per assorbire l'input (e questo registra i tasti) senza effetti collaterali.

Se vuoi fare anche tutte le tput/ resetcose, potresti voler fare una scriptregistrazione per ciascuna delle tue linee e riprodurle, interfogliate con i comandi tput/ reset.


11

Un'altra possibilità è usare Demo Magic , o, per essere più precisi, solo la funzione di stampa di questa raccolta di script, che sostanzialmente equivale a

#!/bin/bash

. ./demo-magic.sh -w2

p "this will look as if typed"

Sotto il cofano, questo utilizza pv , che ovviamente puoi anche usare per ottenere direttamente l'effetto desiderato, la forma di base appare come segue:

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

1
Questo utilizzo di PV è semplicemente fantastico.
Sebastian Stark,

3
Non c'è bisogno di tubi echoa pv, basta usare pv -qL20 <<< "Hello world"se i vostri supporti shell herestrings.
dr01,

8

In linea con il mio nickname posso offrire un'altra soluzione:

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

Sembra strano, no?

  • -MTime::HiRes=usleepimporta la funzione usleep(sospensione in microsecondi) dal Time::HiResmodulo perché il solito sleepaccetta solo secondi interi.
  • -F''divide l'input dato in caratteri (il delimitatore è vuoto '') e mette i caratteri nella matrice @F.
  • BEGIN {$|=1} disabilita il buffering di output in modo che ogni carattere venga immediatamente stampato.
  • for (@F) { print; usleep(100_000+rand(200_000)) } scorre solo i personaggi
  • mettere i caratteri di sottolineatura in numeri è un modo comune per usare una specie di migliaia di separatori in Perl. Sono semplicemente ignorati da Perl, quindi possiamo ad esempio scrivere 1_000(== 1000) o anche 1_0_00se lo consideriamo più facile da leggere.
  • rand() restituisce un numero casuale compreso tra 0 e l'argomento dato, quindi insieme dormono tra 100.000 e 299.999 microsecondi (0,1-0,3 secondi).

Solo per curiosità: rand()restituisce un numero da 0 all'argomento (da 100k a 300k nel tuo esempio) o tra loro (da 100k + 1 a 300k-1 nel tuo esempio)?
dessert

1
Restituisce un numero nell'intervallo [0,200k), cioè includendo 0 ma escludendo 200k. Il comportamento esatto è documentato qui : "Restituisce un numero frazionario casuale maggiore o uguale a 0 e inferiore al valore di EXPR. (EXPR dovrebbe essere positivo.)"
PerlDuck

1
Questo non funziona senza -a e -n
rrauenza

@rrauenza Dal Perl 5.20 lo fa. -Fimplica -ae -aimplica -n.
PerlDuck,

Ah, ok stavo usando 5.16, che è ciò che è su CentOS7.
rrauenza,

6

Un altro strumento che potrebbe funzionare, che non dipende da x11 o altro, è asciicinema . Registra tutto ciò che fai nel tuo terminale e ti consente di riprodurlo come se fosse una cattura dello schermo, solo allora è basato esclusivamente su ASCII! Potrebbe essere necessario disabilitare temporaneamente il prompt per renderlo puramente visivamente pulito. Come altri hanno sottolineato, l'aggiunta di un ritardo consistente non sembrerà naturale e digitarlo da solo potrebbe essere uno degli aspetti più naturali che puoi ottenere.

Dopo aver registrato il testo, puoi fare qualcosa del tipo:

$ asciinema play [your recording].cast; cmatrix

6

Sono sorpreso che nessuno lo abbia ancora menzionato, ma puoi farlo con strumenti di borsa e un ciclo:

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

Passa semplicemente sopra il carattere di input per carattere e li stampa con un ritardo dopo ciascuno. L'unica cosa difficile è che devi impostare il tuo IFS su una stringa vuota in modo che bash non cerchi di dividere i tuoi spazi.

Questa soluzione è semplicissima, quindi l'aggiunta di ritardi variabili tra personaggi, errori di battitura, qualunque cosa sia super facile.

EDIT (grazie, @dessert): Se vuoi un'interfaccia leggermente più naturale, potresti invece farlo

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

Ciò consentirebbe di chiamare la funzione come typeit foo baranziché typeit 'foo bar'. Essere consapevoli del fatto che senza virgolette, gli argomenti sono soggetti alla divisione della parola di bash, quindi ad esempio typeit foo<space><space>barstamperà foo<space>bar. Per preservare gli spazi bianchi, utilizzare le virgolette.


Un buon suggerimento, anche se va notato che si applica la divisione delle parole. Ad esempio, typeit foo<space>barsi tradurrà in foo bar, mentre typeit foo<space><space>barsarà anche causare foo bar. Devi citarlo per assicurarti che sia testuale. @dessert sentiti libero di suggerire una modifica. Posso farcela da solo, ma voglio darti la possibilità di ottenere credito per questo.
whereswalden

+1 per avermi insegnato read -n1(che, tra l'altro, è read -k1in zsh)
Sebastian Stark,

5

Innanzitutto, "sembra che venga digitato, con un ritardo costante tra i personaggi ..." è un po 'contraddittorio, come altri hanno sottolineato. Qualcosa che viene digitato non ha un ritardo consistente. Quando vedi qualcosa prodotto con un ritardo incoerente, otterrai brividi. "Cosa è preso sul mio computer !!! ??!?"

Comunque...

Devo gridare a expect, che dovrebbe essere disponibile sulla maggior parte delle distribuzioni Linux. Vecchia scuola, lo so, ma - supponendo che sia installato - difficilmente potrebbe essere più semplice:

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

Dalla pagina man:

Il flag -h impone che l'output sia inviato (in qualche modo) come un essere umano che sta effettivamente digitando. Ritardi di tipo umano appaiono tra i personaggi. (L'algoritmo si basa su una distribuzione Weibull, con modifiche per adattarsi a questa particolare applicazione.) Questo output è controllato dal valore della variabile "send_human" ...

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.