Como imprimir texto no terminal como se estivesse sendo digitado?

26

Eu tenho uma impressão echo simples que adicionei ao meu .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

Isso imprime uma mensagem no terminal, mas eu quero que pareça que está sendo digitado, com um atraso consistente entre os caracteres.

    
por SimplySimplified 30.04.2018 / 14:37

8 respostas

28

Isso não funciona com Wayland; se você está usando o Ubuntu 17.10 e não mudou para usar o Xorg no login, esta solução não é para você.

Você pode usar xdotool paraisso.Seoatrasoentreasteclasforconsistente,étãosimplesassim:

xdotool type --delay 100 something

Isso digita something com um atraso de 100 milissegundos entre cada pressionamento de tecla.

Se o atraso entre as teclas for aleatório , digamos que de 100 a 300 milissegundos, as coisas ficam um pouco mais complicadas:

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

Esse loop for passa por cada letra da string salva na variável text , imprimindo key <letter> ou key space no caso de um espaço seguido por sleep 0. e um número aleatório entre 1 e 3 ( xdotool ' sleep interpreta o número como segundos). A saída inteira do loop é então canalizada para xdotool , que imprime as letras com o atraso aleatório entre elas. Se você quiser alterar o atraso, basta alterar a (RANDOM%x)+y part, y sendo o menor e x-1+y o limite superior - por 0,2 a 0,5 segundos, seria (RANDOM%4)+2 .

Note que esta abordagem não imprime o texto, mas sim digita exatamente como o usuário faria, sintetizando pressionamentos de tecla simples. Em consequência, o texto é digitado na janela atualmente focalizada; Se você alterar a parte do foco do texto, será digitada na janela recém-focada, que pode ou não ser o que você deseja. Em ambos os casos, dê uma olhada nas outras respostas aqui, todas brilhantes!

    
por dessert 30.04.2018 / 14:45
24

Eu tentei xdotool depois de ler @ resposta da sobremesa, mas não consegui fazê-lo funcionar por algum motivo. Então eu vim com isto:

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

Canalize seu texto para o código acima e ele será impresso como digitado. Você também pode adicionar aleatoriedade substituindo sleep 0.1 por sleep 0.$((RANDOM%3)) .

Versão estendida com erros de digitação falsos

Esta versão apresentará um erro de digitação falso de vez em quando e corrigirá:

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
    
por Sebastian Stark 30.04.2018 / 16:53
18

Você menciona um atraso consistente entre os caracteres, mas se realmente quiser que pareça que está sendo digitado, o tempo não será perfeitamente consistente. Para conseguir isso, você pode gravar sua própria digitação com o comando script e reproduzi-lo com 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 ...

$ 

A gravação é interrompida pressionando CTRL-D.

A passagem do parâmetro -t para script também instrui a geração de informações de tempo, que redirecionei para o arquivo script.timing . Eu passei sed d como um comando para script , pois isso é simplesmente uma maneira de absorver a entrada (e isso registra as teclas digitadas) sem efeitos colaterais.

Se você quiser fazer todo o material tput / reset também, talvez queira fazer uma gravação script para cada uma de suas linhas e reproduzi-las, intercaladas com tput / reset de comandos.

    
por Digital Trauma 30.04.2018 / 19:45
11

Outra possibilidade é usar Demo Magic , ou, para ser mais preciso, apenas a função de impressão dessa coleção de scripts, que basicamente equivale a

#!/bin/bash

. ./demo-magic.sh -w2

p "this will look as if typed"

Sob o capô, isso usa pv , que é claro que você também pode usar para obter diretamente o efeito desejado, a forma básica é a seguinte:

echo "this will look as if typed" | pv -qL 20
    
por godfatherofpolka 30.04.2018 / 21:51
8

De acordo com meu apelido, posso oferecer outra solução:

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

Parece estranho, não é?

  • -MTime::HiRes=usleep importa a função usleep (microsecond sleep) do módulo Time::HiRes porque o usual sleep aceita apenas segundos inteiros.
  • -F'' divide a entrada dada em caracteres (o delimitador está vazio '' ) e coloca os caracteres na matriz @F .
  • BEGIN {$|=1} desabilita o buffer de saída para que cada caractere seja imediatamente impresso.
  • for (@F) { print; usleep(100_000+rand(200_000)) } apenas itera os caracteres
  • colocar sublinhados em números é uma maneira comum de usar alguns tipos de separadores de milhares em Perl. Eles são simplesmente ignorados pelo Perl, então podemos, por exemplo escreva 1_000 (== 1000 ) ou mesmo 1_0_00 se considerarmos que é mais fácil de ler.
  • rand() retorna um número aleatório entre 0 e o argumento dado, então juntos isso dorme entre 100.000 e 299.999 microssegundos (0,1-0,3 segundos).
por PerlDuck 30.04.2018 / 19:20
6

Outra ferramenta que pode funcionar, que não depende do x11 ou de qualquer outro, é asciicinema . Ele grava tudo o que você faz no seu terminal e permite que você repita que, como se fosse uma captura de tela, só então ele é puramente baseado em ascii! Você pode ter que desativar temporariamente o seu prompt, no entanto, para que ele seja puramente visualmente limpo. Como outros apontaram, adicionar um atraso consistente não parecerá natural, e digitá-lo você mesmo pode ser um dos aspectos mais naturais que você pode alcançar.

Depois de gravar o texto, você pode fazer algo como:

$ asciinema play [your recording].cast; cmatrix
    
por rien333 02.05.2018 / 15:31
6

Estou surpreso que ninguém tenha mencionado isso ainda, mas você pode fazer isso com ferramentas de estoque e um loop:

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

Ele apenas faz um loop sobre o caractere de entrada por caractere e os imprime com um atraso após cada um. O único truque é que você precisa configurar seu IFS para uma string vazia, então o bash não tenta dividir seus espaços.

Essa solução é simples, adicionando atrasos variáveis entre caracteres, erros de digitação, o que é super fácil.

EDIT (obrigado, @dessert): Se você quer uma interface um pouco mais natural, você poderia fazer

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

Isso permitiria chamar a função como typeit foo bar em vez de typeit 'foo bar' . Esteja ciente de que, sem as aspas, os argumentos estão sujeitos à divisão de palavras do bash, portanto, por exemplo, typeit foo<space><space>bar imprimirá foo<space>bar . Para preservar o espaço em branco, use aspas.

    
por whereswalden 01.05.2018 / 04:04
5

Primeiro, "parece que está sendo digitado, com um atraso consistente entre os personagens ..." é um pouco contraditório, como outros apontaram. Algo sendo digitado não tem um atraso consistente. Quando você vê algo produzido com um atraso inconsistente, você vai ter arrepios. "O que é o meu computador? !!!?"

De qualquer forma ...

Eu tenho que gritar para expect , que deve estar disponível na maioria das distribuições Linux. Old School, eu sei, mas, supondo que esteja instalado, dificilmente poderia ser mais simples:

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

Da página do manual:

The -h flag forces output to be sent (somewhat) like a human actually typing. Human-like delays appear between the characters. (The algorithm is based upon a Weibull distribution, with modifications to suit this particular application.) This output is controlled by the value of the variable "send_human"...

Veja o link

    
por Mike S 02.05.2018 / 14:53