Terminaldeki metin yazıldığı gibi nasıl yazdırılır?


27

Benim echoeklediğim basit bir çıktı var .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

Bu, terminale bir mesaj yazdırır, ancak karakterlerin arasında tutarlı bir gecikmeyle yazılmış gibi görünmesini istiyorum.


1
Gerçekçi olmak için, düzeltmek için bir veya daha fazla arka boşlukla rastgele yazım hatası almanız gerektiğini düşünüyorum. Örneğin, bu yorumu yazarken, "atıldı" düzeltmek için boşluktan "yoluyla" geri geçmek zorundaydım. Cevaplaması daha zor bir soru yapıyor ama daha gerçekçi kılıyor. Karakterler sürekli olarak 30 CPS veya 60 CPS hızında tekrarlanırsa daha az insan görünür. Bazı tuşlar birlikte daha hızlı yazılır, diğer tuş kombinasyonları daha yavaş görünür. Tuş kombinasyonlarının hız ile eşleştirilmesi için bir çeşit desen gereklidir.
WinEunuuchs2Unix

2
@ WinEunuuchs2Unix Bunun hala SimplySimplified olduğunu sanmıyorum. ;)
tatlı

Yanıtlar:


28

Bu Wayland ile çalışmıyor; Ubuntu 17.10 kullanıyorsanız ve giriş sırasında Xorg'u kullanmadıysanız, bu çözüm sizin için değildir.

Bunun için kullanabilirsiniz xdotool Xdotool yükle. Tuş vuruşları arasındaki gecikme tutarlıysa , bu kadar basittir:

xdotool type --delay 100 something

Bu something, 100her tuş vuruşu arasında milisaniye gecikmesi olan türler .


Tuş vuruşları arasındaki gecikmenin rastgele olması gerekiyorsa , diyelim ki 100 ila 300 milisaniye, işler biraz daha karmaşıklaşıyor:

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

Bu fordöngü değişkende kaydedilen dizgenin her bir harfinden geçer, ardından textya key <letter>da key spaceardından gelen bir boşlukta basılır sleep 0.ve 1 ile 3 arasında rasgele bir sayı ( xdotool's sleep, sayıyı saniye olarak yorumlar). Sonra, döngünün tüm çıktısı, xdotoolaradaki rastgele gecikmeyle harfleri basan şekilde bağlanır. Gecikmeyi değiştirmek istiyorsanız sadece parçayı değiştirin, alt ve üst limit olmak üzere - 0,2 ile 0,5 saniye arasında olacaktır .(RANDOM%x)+yyx-1+y(RANDOM%4)+2

Bu yaklaşım unutmayın yazdırmak metni değil, yazın tek basılmasını sentezleyerek, tam olarak kullanıcı yapacağını gibi. Sonuç olarak, metin o andaki odaklanmış pencereye girilir; metnin odak kısmını değiştirirseniz, istediğiniz gibi olabilir veya olmayabilir yeni odaklanmış pencerede yazılacaktır. Her iki durumda da buradaki diğer cevaplara bir göz atın, hepsi mükemmel!


24

@ Tatlı cevabı okuduktan sonra xdotool denedim ama bir nedenden dolayı işe alamadı. Bu yüzden bu geldi:

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

Metninizi yukarıdaki koda aktarın ve yazdığınız gibi yazdırılacaktır. Ayrıca sleep 0.1ile değiştirerek rastgelelik ekleyebilirsiniz sleep 0.$((RANDOM%3)).

Sahte yazım hatası içeren genişletilmiş sürüm

Bu sürüm her şimdi ve sonra sahte bir yazım hatası getirecek ve düzeltecek:

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

Bunun yerine bunu yapacağımı düşünüyorum: while IFS= read -r line; do for (( i = 0; i < ${#line}; i++ )); do sleep 0.1; printf "%s" "${line:i:1}"; done; echo; done( ;gerekirse yeni satırlarla ve iyi girinti ile değiştirin ). IFS= read -rVe printf "%s"boşluk ve özel karakterler herhangi farklı muamele değil emin olun. Ve grepkarakterlere ayırmak için her satırda gerekli değildir - sadece forsatırdaki her karakterin üzerinde bir döngü yeterlidir.
Dijital Travma

18

Karakterler arasında tutarlı bir gecikmeden bahsediyorsunuz, ancak gerçekten yazılmış gibi görünmesini istiyorsanız, zamanlama tam olarak tutarlı olmaz. Bunu başarmak için scriptkomutla kendi yazdığınızı kaydedebilir ve aşağıdakilerle oynatabilirsiniz 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 ...

$ 

CTRL-D tuşuna basılarak kayıt durdurulur.

Geçme -tparametreyi scriptde ben yönlendirilmesini zamanlama bilgisini oluşturmak talimatını script.timingdosyası. Bu sadece basit bir yan etki olmadan girdiyi absorbe etmenin bir yolu olduğundan (ve bu tuş vuruşlarını kaydederken) sed dkomut olarak geçtim script.

Hepsini tput/ resetşeyler de yapmak istiyorsanız, scripther satırınız için bir kayıt yapmak ve tput/ resetkomutları ile iç içe geçmiş, onları oynatmak isteyebilirsiniz .


11

Başka bir olasılık Demo Magic kullanmak veya bu senaryo koleksiyonunun sadece basma fonksiyonunun daha kesin olması için kullanmaktır.

#!/bin/bash

. ./demo-magic.sh -w2

p "this will look as if typed"

Başlık altında, bu pv'yi kullanır , elbette istenen efekti elde etmek için doğrudan kullanabilirsiniz, temel form aşağıdaki gibidir:

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

1
Pv'nin bu kullanımı sadece harika.
Sebastian Stark,

3
Boruya gerek yok echoetmek pv, sadece kullanmak pv -qL20 <<< "Hello world"Kabuk destekleri herestrings eğer.
dr01

8

Takma adım doğrultusunda başka bir çözüm önerebilirim:

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

Garip görünüyor, değil mi?

  • -MTime::HiRes=usleep işlevi alır usleep (mikrosaniye uykusu) Time::HiResmodülden çünkü olağan sleepsadece tamsayıları kabul eder.
  • -F'' verilen girişi karakterlere böler (sınırlayıcı boş '' ) ve karakterleri diziye koyar @F.
  • BEGIN {$|=1} Çıktı tamponlamayı devre dışı bırakır, böylece her karakter anında yazdırılır.
  • for (@F) { print; usleep(100_000+rand(200_000)) } sadece karakterlerin üzerinden geçiyor
  • Sayılara alt çizgi koymak, Perl'de binlerce tür ayırıcı kullanmanın yaygın bir yoludur. Onlar sadece Perl tarafından yok sayılırlar, bu yüzden örneğin yazabiliriz 1_000(== 1000) veya hatta1_0_00 okunması daha kolay .
  • rand() 0 ile verilen argüman arasında rasgele bir sayı döndürür, bu yüzden birlikte bu 100.000 ila 299.999 mikrosaniye (0.1-0.3 saniye) arasında kalır.

Sadece meraktan: rand()0 dan argümana (örneğin 100k ila 300k) veya aralarında ( örneğin, 100k + 1 ila 300k-1 ) arasında bir sayı döndürür mü?
tatlılar

1
Aralık dahilinde bir sayı döndürür [0,200k), yani 0 dahil ancak 200k hariç. Kesin davranış burada belgelenmiştir : "0'dan büyük veya 0'a eşit ve EXPR'nin değerinden küçük (EXPR pozitif olmalıdır) rastgele kesirli bir sayı döndürür."
PerlDuck

1
Bu -a ve -n olmadan çalışmaz
rrauenza

@rrauenza Perl'den beri 5.20 . -Fima eder -ave -aima eder -n.
PerlDuck

Ah, tamam 5.16 kullanıyordum, yani CentOS7'de olan.
rrauenza

6

İşe yarayabilecek başka bir araç, x11'e ya da her neye bağlı değildir, asidendir . Terminalinizde yaptığınız her şeyi kaydeder ve bir ekran görüntüsümüş gibi tekrar etmenize izin verir, ancak o zaman tamamen asci tabanlı! Tamamen görsel olarak temiz olması için isteminizi geçici olarak devre dışı bırakmanız gerekebilir. Diğerlerinin de belirttiği gibi, tutarlı bir gecikme eklemek doğal görünmeyecek ve onu kendiniz yazmak, elde edebileceğiniz en doğal görünümlerden biri olabilir.

Metni kaydettikten sonra, şöyle bir şey yapabilirsiniz:

$ asciinema play [your recording].cast; cmatrix

6

Henüz kimseden bahsetmediğinden şaşırdım, ancak bunu stok araçları ve bir döngü ile başarabilirsiniz:

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

Sadece giriş karakterini karakter karakterinde döndürür ve her biri bir gecikmeyle yazdırır. Tek zorlu bit, IFS'nizi boş bir dizeye ayarlamanız gerektiği, böylece bash boşluklarınızı ayırmaya çalışmaz.

Bu çözüm çok basittir, bu nedenle karakterlerin, yazım hatalarının, her ne süper kolay ise, değişken gecikmeler eklenebilir.

EDIT (teşekkürler, @ tatlı): Biraz daha doğal bir arayüz istiyorsanız, bunun yerine

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

Bu, işlevi typeit foo baryerine çağırmanıza izin verir typeit 'foo bar'. Örnek için çok tırnak işaretleri olmadan, argümanlar, bash kelime bölme tabi olduğunu unutmayın typeit foo<space><space>baryazdırılacaktır foo<space>bar. Boşluğu korumak için tırnak işaretleri kullanın.


İyi öneri, kelime bölmenin geçerli olduğu not edilmelidir. Örneğin, typeit foo<space>barsonuçlanır foo bar, oysa typeit foo<space><space>barolacak da neden foo bar. Sözlü olduğundan emin olmak için alıntı yapmalısın. @dessert bir düzenleme önermek için çekinmeyin. Kendim yapabilirim ama sana kredi alma şansı vermek istiyorum.
18’de

read -n1(Btw. read -k1Zsh cinsinden ) hakkında bana öğretmek için +1
Sebastian Stark

5

İlk olarak, "yazıyormuş gibi görünüyorsun, karakterler arasında tutarlı bir gecikmeyle ..." diğerlerinin de belirttiği gibi biraz çelişkili. Yazılan bir şeyin tutarlı bir gecikmesi yoktur. Tutarsız bir gecikmeyle üretilen bir şey gördüğünüzde titremeye başlarsınız. "Bilgisayarımdan ne geçti !!! ??!?"

Neyse ...

expectÇoğu Linux dağıtımında erişilebilir olması gereken bir haykırış yapmalıyım. Old School, biliyorum, ama yüklü olduğunu varsayarsak daha kolay olamazdı:

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

Man sayfasından:

-H bayrağı çıktısını, bir insanın yazdığı gibi (biraz) göndermeye zorlar. İnsan benzeri gecikmeler karakterler arasında görülür. (Algoritma, bu özel uygulamaya uyabilecek modifikasyonlarla birlikte bir Weibull dağılımına dayanmaktadır.) Bu çıkış, "send_human" değişkeninin değeri ile kontrol edilir ...

Bakınız https://www.tcl.tk/man/expect5.31/expect.1.html

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.