Làm thế nào để in văn bản trong thiết bị đầu cuối như thể nó được gõ?


27

Tôi có một echobản in đơn giản mà tôi đã thêm vào .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

Điều này in một thông báo đến thiết bị đầu cuối, nhưng tôi muốn nó trông như thể nó được gõ, với độ trễ nhất quán giữa các ký tự.


1
Để thực tế, tôi nghĩ rằng bạn nên có một lỗi đánh máy ngẫu nhiên với một hoặc nhiều khoảng trắng phía sau để sửa. Ví dụ, trong khi gõ nhận xét này, tôi phải sao lưu khoảng "qua" để sửa nó thành "ném". Nó làm cho nó trở thành một câu hỏi khó trả lời hơn nhưng nó làm cho nó thực tế hơn. Nếu các ký tự lặp lại ở tốc độ ổn định 30 CPS hoặc 60 CPS, nó trông giống con người hơn. Một số phím nhất định được gõ nhanh hơn với nhau, trong khi các tổ hợp phím khác xuất hiện chậm hơn. Một số loại khớp mẫu của các tổ hợp phím theo tốc độ là cần thiết.
WinEunuuchs2Unix

2
@ WinEunuuchs2Unix Tôi không nghĩ rằng điều này vẫn chỉ đơn giản là Đơn giản hóa. ;)
tráng miệng

Câu trả lời:


28

Điều này không hoạt động với Wayland; nếu bạn đang sử dụng Ubuntu 17.10 và không thay đổi sử dụng Xorg khi đăng nhập, giải pháp này không dành cho bạn.

Bạn có thể sử dụng xdotool Cài đặt xdotoolcho điều đó. Nếu độ trễ giữa các lần nhấn phím phải nhất quán , thì đơn giản như sau:

xdotool type --delay 100 something

Loại này somethingcó độ trễ là 100mili giây giữa mỗi lần nhấn phím.


Nếu độ trễ giữa các lần nhấn phím là ngẫu nhiên , giả sử từ 100 đến 300 mili giây, mọi thứ sẽ phức tạp hơn một chút:

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

forVòng lặp này đi qua từng chữ cái duy nhất của chuỗi được lưu trong biến text, in key <letter>hoặc key spacetrong trường hợp khoảng trắng theo sau sleep 0.và một số ngẫu nhiên trong khoảng từ 1 đến 3 ( xdotool's sleepdiễn giải số là giây). Toàn bộ đầu ra của vòng lặp sau đó được dẫn đến xdotool, in ra các chữ cái có độ trễ ngẫu nhiên ở giữa. Nếu bạn muốn thay đổi độ trễ, chỉ cần thay đổi phần, là giới hạn dưới và giới hạn trên - trong 0,2 đến 0,5 giây .(RANDOM%x)+yyx-1+y(RANDOM%4)+2

Lưu ý rằng phương pháp này không in văn bản, mà chỉ chính xác như người dùng sẽ làm, tổng hợp các phím bấm đơn. Do đó, văn bản được gõ vào cửa sổ hiện đang tập trung; nếu bạn thay đổi phần tiêu điểm của văn bản sẽ được gõ vào cửa sổ mới được tập trung, đây có thể là hoặc không phải là điều bạn muốn. Trong cả hai trường hợp, hãy xem các câu trả lời khác ở đây, tất cả đều tuyệt vời!


24

Tôi đã thử xdotool sau khi đọc câu trả lời của @ Dessert nhưng không thể khiến nó hoạt động được vì một số lý do. Vì vậy, tôi đã đưa ra điều này:

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

Đưa văn bản của bạn vào đoạn mã trên và nó sẽ được in như gõ. Bạn cũng có thể thêm ngẫu nhiên bằng cách thay thế sleep 0.1bằng sleep 0.$((RANDOM%3)).

Phiên bản mở rộng với lỗi chính tả

Phiên bản này sẽ giới thiệu một lỗi đánh máy giả mỗi giờ và sau đó sửa nó:

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

Tôi nghĩ rằng tôi sẽ làm điều này thay vào đó: while IFS= read -r line; do for (( i = 0; i < ${#line}; i++ )); do sleep 0.1; printf "%s" "${line:i:1}"; done; echo; done(thay thế ;bằng dòng mới và thụt lề tốt khi cần thiết). Các IFS= read -rprintf "%s"đảm bảo rằng khoảng trắng và ký tự đặc biệt không được điều trị bất kỳ khác nhau. Và greptrên mỗi dòng để phân chia thành các ký tự là không cần thiết - chỉ cần một forvòng lặp trên mỗi char trong dòng là đủ.
Chấn thương kỹ thuật số

18

Bạn đề cập đến độ trễ nhất quán giữa các ký tự, nhưng nếu bạn thực sự muốn nó trông giống như được gõ, thời gian sẽ không hoàn toàn nhất quán. Để đạt được điều này, bạn có thể ghi lại cách gõ của riêng mình bằng scriptlệnh và phát lại bằng 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 ...

$ 

Ghi âm bị dừng bằng cách nhấn CTRL-D.

Truyền -ttham số để scripthướng dẫn nó cũng tạo ra thông tin về thời gian mà tôi đã chuyển hướng đến script.timingtệp. Tôi đã thông qua sed dnhư một lệnh để scriptchỉ đơn giản là một cách để hấp thụ đầu vào (và điều này ghi lại các lần nhấn phím) mà không có tác dụng phụ.

Nếu bạn cũng muốn thực hiện tất cả các công cụ tput/ reset, bạn có thể muốn thực hiện scriptghi âm cho từng dòng của mình và phát lại chúng, xen kẽ với các lệnh tput/ reset.


11

Một khả năng khác là sử dụng Demo Magic , hoặc, chính xác hơn là chức năng in của bộ sưu tập tập lệnh này, về cơ bản lên tới

#!/bin/bash

. ./demo-magic.sh -w2

p "this will look as if typed"

Dưới mui xe, điều này sử dụng pv , tất nhiên bạn cũng có thể sử dụng để trực tiếp có được hiệu ứng mong muốn, hình thức cơ bản trông như sau:

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

1
Cách sử dụng này của pv chỉ là tuyệt vời.
Sebastian Stark

3
Không cần phải dẫn echođến pv, chỉ cần sử dụng pv -qL20 <<< "Hello world"nếu vỏ của bạn hỗ trợ các herestrings.
dr01

8

Để phù hợp với biệt danh của tôi, tôi có thể đưa ra một giải pháp khác:

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

Trông lạ nhỉ?

  • -MTime::HiRes=usleepnhập hàm usleep(ngủ micro giây) từ Time::HiResmô-đun vì thông thường sleepchỉ chấp nhận số nguyên giây.
  • -F''chia đầu vào đã cho thành các ký tự (dấu phân cách trống '') và đặt các ký tự trong mảng @F.
  • BEGIN {$|=1} vô hiệu hóa bộ đệm đầu ra để mỗi ký tự được in ngay lập tức.
  • for (@F) { print; usleep(100_000+rand(200_000)) } chỉ lặp đi lặp lại qua các nhân vật
  • đặt dấu gạch dưới vào số là một cách phổ biến để sử dụng một số loại hàng ngàn dấu phân cách trong Perl. Chúng đơn giản bị bỏ qua bởi Perl, vì vậy chúng ta có thể viết 1_000(== 1000) hoặc ngay cả 1_0_00khi chúng ta xem nó dễ đọc hơn.
  • rand() trả về một số ngẫu nhiên trong khoảng từ 0 đến đối số đã cho, do đó, số này sẽ nằm trong khoảng từ 100.000 đến 299.999 micro giây (0,1-0,3 giây).

Chỉ tò mò: Có rand()trả lại một số từ 0 cho đối số (100k đến 300k trong ví dụ của bạn) hoặc giữa chúng (100k + 1 đến 300k-1 trong ví dụ của bạn) không?
tráng miệng

1
Nó trả về một số trong khoảng [0,200k), tức là bao gồm 0 nhưng không bao gồm 200k. Hành vi chính xác được ghi lại ở đây : "Trả về một số phân số ngẫu nhiên lớn hơn hoặc bằng 0 và nhỏ hơn giá trị của EXPR. (EXPR nên dương.)"
PerlDuck

1
Điều này không hoạt động mà không có -a và -n
rrauenza

@rrauenza Kể từ Perl 5.20 . -Fngụ ý -a-angụ ý -n.
PerlDuck

À, ok tôi đã sử dụng 5.16, đó là những gì có trên CentOS7.
rrauenza

6

Một công cụ khác có thể hoạt động, không phụ thuộc vào x11 hoặc bất cứ điều gì, là asciicinema . Nó ghi lại mọi thứ bạn làm trong thiết bị đầu cuối của bạn và cho phép bạn phát lại như thể đó là một ảnh chụp màn hình, chỉ sau đó nó hoàn toàn dựa trên ascii! Bạn có thể phải tạm thời vô hiệu hóa lời nhắc của mình tuy nhiên để nó hoàn toàn sạch sẽ. Như những người khác đã chỉ ra, việc thêm một độ trễ nhất quán sẽ không tự nhiên và tự gõ nó có thể là một trong những giao diện tự nhiên nhất bạn có thể đạt được.

Sau khi đã ghi lại văn bản, bạn có thể làm một cái gì đó như:

$ asciinema play [your recording].cast; cmatrix

6

Tôi ngạc nhiên không ai đề cập đến điều này, nhưng bạn có thể thực hiện điều này với các công cụ chứng khoán và một vòng lặp:

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

Nó chỉ lặp lại ký tự đầu vào theo ký tự và in chúng ra với độ trễ sau mỗi ký tự. Điều khó khăn duy nhất là bạn phải đặt IFS của mình thành một chuỗi trống để bash không cố tách các khoảng trắng của bạn.

Giải pháp này rất đơn giản, vì vậy việc thêm độ trễ thay đổi giữa các ký tự, lỗi chính tả, bất cứ điều gì là siêu dễ dàng.

EDIT (cảm ơn, @d Portable): Nếu bạn muốn giao diện tự nhiên hơn một chút, thay vào đó bạn có thể làm

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

Điều này sẽ cho phép bạn gọi các chức năng như typeit foo barlà hơn typeit 'foo bar'. Xin lưu ý rằng không có dấu ngoặc kép, các đối số có thể bị tách từ bash, vì vậy, ví dụ typeit foo<space><space>barsẽ in foo<space>bar. Để giữ khoảng trắng, sử dụng dấu ngoặc kép.


Đề nghị tốt, mặc dù cần lưu ý rằng áp dụng chia tách từ. Ví dụ, typeit foo<space>barsẽ dẫn đến foo bar, trong khi đó cũngtypeit foo<space><space>bar sẽ dẫn đến . Bạn phải trích dẫn nó để đảm bảo nguyên văn. @dPlay hãy đề nghị chỉnh sửa. Tôi có thể tự làm điều đó, nhưng tôi muốn cho bạn cơ hội nhận được tín dụng cho nó. foo bar
theo đó,

+1 để dạy tôi về read -n1(trong đó, btw. Đang read -k1ở zsh)
Sebastian Stark

5

Đầu tiên, "trông như thể nó đang được gõ, với độ trễ nhất quán giữa các ký tự ..." là một chút mâu thuẫn, như những người khác đã chỉ ra. Một cái gì đó được gõ không có một độ trễ nhất quán. Khi bạn thấy một thứ gì đó được tạo ra với độ trễ không nhất quán, bạn sẽ bị ớn lạnh. "Cái gì đã chiếm lấy máy tính của tôi !!! ??!?"

Dù sao...

Tôi phải hét to lên expect, điều này nên có trên hầu hết các bản phân phối Linux. Trường học cũ, tôi biết, nhưng - giả sử nó đã được cài đặt - nó khó có thể đơn giản hơn:

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

Từ trang người đàn ông:

Cờ -h buộc đầu ra được gửi (phần nào) giống như con người thực sự gõ. Sự chậm trễ giống như con người xuất hiện giữa các nhân vật. (Thuật toán dựa trên phân phối Weibull, với các sửa đổi cho phù hợp với ứng dụng cụ thể này.) Đầu ra này được kiểm soát bởi giá trị của biến "send_human" ...

Xem https://www.tcl.tk/man/Exect5.31/Exect.1.html

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.