Bagaimana cara mencetak teks di terminal seolah sedang diketik?

27

Saya memiliki echocetakan sederhana yang telah saya tambahkan ke .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

Ini mencetak pesan ke terminal, tapi saya ingin itu terlihat seperti sedang diketik, dengan penundaan yang konsisten antara karakter.

SederhanaSederhana
sumber
1
Untuk menjadi realistis saya pikir Anda harus memiliki kesalahan ketik acak dilemparkan dengan satu ruang belakang atau lebih untuk memperbaikinya. Misalnya saat mengetik komentar ini saya harus kembali spasi "melalui" untuk memperbaikinya menjadi "dibuang". Itu membuatnya menjadi pertanyaan yang lebih sulit untuk dijawab tetapi membuatnya lebih realistis. Jika karakter mengulangi dengan kecepatan tetap 30 CPS atau 60 CPS, karakter itu terlihat kurang manusiawi. Beberapa tombol diketik lebih cepat bersamaan, sementara kombinasi tombol lainnya tampak lebih lambat. Diperlukan semacam pencocokan pola kombinasi tombol dengan kecepatan.
WinEunuuchs2Unix
2
@ WinEunuuchs2Unix Saya tidak berpikir ini masih SimplySimplified. ;)
hidangan penutup
1
Lihat jawaban saya di sini
Dijeda sampai pemberitahuan lebih lanjut.

Jawaban:

28

Ini tidak bekerja dengan Wayland; jika Anda menggunakan Ubuntu 17.10 dan tidak berubah untuk menggunakan Xorg saat masuk, solusi ini bukan untuk Anda.

Anda bisa menggunakannya xdotool Instal xdotooluntuk itu. Jika penundaan antara penekanan tombol harus konsisten , sesederhana itu:

xdotool type --delay 100 something

Jenis ini somethingdengan penundaan 100milidetik di antara setiap penekanan tombol.


Jika penundaan antara penekanan tombol harus acak , katakanlah 100 hingga 300 milidetik, segalanya menjadi sedikit lebih rumit:

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

Ini forlingkaran melewati setiap huruf dari string disimpan dalam variabel text, mencetak baik key <letter>atau key spacedalam kasus ruang diikuti oleh sleep 0.dan nomor acak antara 1 dan 3 ( xdotool's sleepmenafsirkan jumlah sebagai detik). Seluruh output dari loop kemudian disalurkan ke xdotool, yang mencetak huruf-huruf dengan penundaan acak di antaranya. Jika Anda ingin mengubah penundaan, ubah saja bagiannya, menjadi batas bawah dan atas - selama 0,2 hingga 0,5 detik .(RANDOM%x)+yyx-1+y(RANDOM%4)+2

Perhatikan bahwa pendekatan ini tidak mencetak teks, tetapi mengetiknya persis seperti yang akan dilakukan pengguna, mensintesis penekanan tombol tunggal. Karena itu, teks akan diketik ke dalam jendela yang saat ini difokuskan; jika Anda mengubah bagian fokus dari teks akan diketik di jendela yang baru difokuskan, yang mungkin atau mungkin tidak seperti yang Anda inginkan. Dalam kedua kasus, lihat jawaban lain di sini, yang semuanya brilian!

pencuci mulut
sumber
24

Saya mencoba xdotool setelah membaca jawaban @ hidangan penutup tetapi tidak berhasil karena beberapa alasan. Jadi saya datang dengan ini:

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

Pipa teks Anda ke dalam kode di atas dan itu akan dicetak seperti diketik. Anda juga dapat menambahkan keacakan dengan mengganti sleep 0.1dengan sleep 0.$((RANDOM%3)).

Versi diperpanjang dengan kesalahan ketik palsu

Versi ini akan memperkenalkan kesalahan ketik sesekali dan memperbaikinya:

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
Sebastian Stark
sumber
Saya pikir saya akan melakukan ini sebagai gantinya: while IFS= read -r line; do for (( i = 0; i < ${#line}; i++ )); do sleep 0.1; printf "%s" "${line:i:1}"; done; echo; done(ganti ;dengan baris baru dan indentasi yang baik sebagaimana diperlukan). The IFS= read -rdan printf "%s"memastikan spasi dan karakter khusus tidak diperlakukan secara berbeda. Dan greppada setiap baris untuk dibagi menjadi karakter tidak perlu - cukup satu forlingkaran di atas setiap karakter di baris sudah cukup.
Trauma Digital
18

Anda menyebutkan penundaan yang konsisten antara karakter, tetapi jika Anda benar-benar ingin itu terlihat seperti sedang diketik, waktunya tidak akan sepenuhnya konsisten. Untuk mencapai ini, Anda dapat merekam pengetikan Anda sendiri dengan scriptperintah dan memainkannya kembali dengan 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 ...

$ 

Perekaman dihentikan dengan menekan CTRL-D.

Melewati -tparameter untuk scriptmenginstruksikannya juga menghasilkan informasi waktu, yang telah saya arahkan ke script.timingfile. Saya telah menyampaikan sed dsebagai perintah scriptkarena ini hanyalah cara untuk menyerap input (dan ini merekam penekanan tombol) tanpa efek samping.

Jika Anda ingin melakukan semua tput/ resethal juga, Anda mungkin ingin melakukan scriptrekaman untuk setiap baris Anda, dan memutarnya kembali, disisipkan dengan perintah tput/ reset.

Trauma Digital
sumber
11

Kemungkinan lain adalah menggunakan Demo Magic , atau, lebih tepatnya hanya fungsi cetak kumpulan skrip ini, yang pada dasarnya berjumlah

#!/bin/bash

. ./demo-magic.sh -w2

p "this will look as if typed"

Di bawah tenda, ini menggunakan pv , yang tentu saja Anda juga dapat menggunakan untuk langsung mendapatkan efek yang diinginkan, bentuk dasar terlihat sebagai berikut:

echo "this will look as if typed" | pv -qL 20
godfatherofpolka
sumber
1
Penggunaan pv ini sangat bagus.
Sebastian Stark
3
Tidak perlu pipa echountuk pv, hanya menggunakan pv -qL20 <<< "Hello world"jika herestrings mendukung shell Anda.
dr01
8

Sejalan dengan nama panggilan saya, saya dapat menawarkan solusi lain:

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

Terlihat aneh, bukan?

  • -MTime::HiRes=usleepmengimpor fungsi usleep(mikrodetik tidur) dari Time::HiResmodul karena biasanya sleephanya menerima detik integer.
  • -F''membagi input yang diberikan ke karakter (pembatas kosong '') dan menempatkan karakter dalam array @F.
  • BEGIN {$|=1} menonaktifkan buffering output sehingga setiap karakter segera dicetak.
  • for (@F) { print; usleep(100_000+rand(200_000)) } hanya mengulangi karakter
  • menempatkan garis bawah dalam angka adalah cara umum untuk menggunakan beberapa jenis pemisah ribuan di Perl. Mereka hanya diabaikan oleh Perl, jadi kita bisa misal menulis 1_000(== 1000) atau bahkan 1_0_00jika kita anggap lebih mudah dibaca.
  • rand() mengembalikan angka acak antara 0 dan argumen yang diberikan, jadi bersama-sama ini tidur antara 100.000 dan 299.999 mikrodetik (0,1-0,3 detik).
PerlDuck
sumber
Hanya ingin tahu: Apakah rand()mengembalikan angka dari 0 ke argumen (100k ke 300k dalam contoh Anda) atau di antara mereka (100k + 1 hingga 300k-1 dalam contoh Anda)?
hidangan penutup
1
Ini mengembalikan angka dalam interval [0,200k), yaitu termasuk 0 tetapi tidak termasuk 200rb. Perilaku yang tepat didokumentasikan di sini : "Mengembalikan bilangan pecahan acak lebih besar dari atau sama dengan 0 dan kurang dari nilai EXPR. (EXPR harus positif.)"
PerlDuck
1
Ini tidak akan berhasil tanpa -a dan -n
rrauenza
@rrauenza Sejak Perl 5.20 ya. -Fmenyiratkan -adan -amenyiratkan -n.
PerlDuck
Ah, ok saya menggunakan 5.16, yang ada di CentOS7.
rrauenza
6

Alat lain yang mungkin berfungsi, yang tidak bergantung pada x11 atau apa pun, adalah asciicinema . Ini merekam semua yang Anda lakukan di terminal Anda dan memungkinkan Anda memutar ulang bahwa seolah-olah itu adalah tangkapan layar, hanya itu murni berdasarkan ascii! Namun, Anda mungkin harus menonaktifkan sementara prompt Anda agar benar-benar bersih secara visual. Seperti yang telah ditunjukkan oleh orang lain, menambahkan penundaan yang konsisten tidak akan terlihat alami, dan mengetiknya sendiri mungkin salah satu penampilan paling alami yang dapat Anda capai.

Setelah merekam teks, Anda dapat melakukan sesuatu seperti:

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

Saya terkejut belum ada yang menyebutkan ini, tetapi Anda dapat melakukannya dengan alat stok dan loop:

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

Itu hanya loop atas karakter input oleh karakter dan mencetaknya dengan penundaan setelah masing-masing. Satu-satunya bit yang rumit adalah bahwa Anda harus mengatur IFS Anda ke string kosong sehingga bash tidak mencoba untuk memisahkan ruang Anda.

Solusi ini sangat sederhana, sehingga menambahkan penundaan variabel antara karakter, kesalahan ketik, apa pun yang super mudah.

EDIT (terima kasih, @dabut): Jika Anda ingin antarmuka yang sedikit lebih alami, Anda bisa melakukannya

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

Ini akan memungkinkan Anda untuk memanggil fungsi typeit foo bardaripada typeit 'foo bar'. Ketahuilah bahwa tanpa tanda kutip, argumennya tunduk pada pemisahan kata bash, jadi misalnya typeit foo<space><space>barakan dicetak foo<space>bar. Untuk mempertahankan spasi putih, gunakan kutipan.

ketika tiba
sumber
Saran yang bagus, meskipun harus dicatat bahwa pemisahan kata berlaku. Misalnya, typeit foo<space>barakan menghasilkan foo bar, sedangkan jugatypeit foo<space><space>bar akan menghasilkan . Anda harus mengutipnya untuk memastikan itu kata demi kata. @dabut ragu untuk menyarankan hasil edit. Saya bisa melakukannya sendiri, tetapi saya ingin memberi Anda kesempatan untuk mendapatkan kredit untuk itu. foo bar
whereswalden
+1 untuk mengajari saya tentang read -n1(yang, btw. Ada read -k1di zsh)
Sebastian Stark
5

Pertama, "terlihat seolah-olah sedang diketik, dengan penundaan yang konsisten antara karakter ..." sedikit kontradiktif, seperti yang orang lain tunjukkan. Sesuatu yang diketik tidak memiliki penundaan yang konsisten. Ketika Anda melihat sesuatu diproduksi dengan penundaan yang tidak konsisten, Anda akan kedinginan. "Apa yang mengambil alih komputer saya !!! ??!?"

Bagaimanapun...

Saya harus berteriak expect, yang seharusnya tersedia di sebagian besar distribusi Linux. Old School, saya tahu, tetapi - dengan anggapan itu terinstal - tidak mungkin lebih sederhana:

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

Dari halaman manual:

Bendera -h memaksa output untuk dikirim (agak) seperti manusia yang benar-benar mengetik. Keterlambatan mirip manusia muncul di antara karakter. (Algoritma ini didasarkan pada distribusi Weibull, dengan modifikasi yang sesuai dengan aplikasi khusus ini.) Output ini dikendalikan oleh nilai variabel "send_human" ...

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

Mike S
sumber