如何像在键入文字一样在终端中打印文字?


27

echo已经添加了一个简单的打印件.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

这会向终端打印一条消息,但我希望它看起来像是在键入,字符之间的延迟是一致的。


1
现实地说,我认为您应该输入随机错字并带有一个或多个退格键来更正。例如,在键入此注释时,我必须在“直通”上退格以将其更正为“抛出”。这使它成为一个更难回答的问题,但也使它变得更加现实。如果角色以30 CPS或60 CPS的稳定速度重复播放,则看起来不那么人性化。某些键的键入速度更快,而其他键的组合则显示速度较慢。需要某种形式的按键组合与速度的匹配。
WinEunuuchs2Unix '18年

2
@ WinEunuuchs2Unix我不认为这仍然是SimplySimplified。;)
甜点

Answers:


28

这不适用于Wayland;如果您使用的是Ubuntu 17.10,并且在登录时未更改为使用Xorg,则此解决方案不适合您。

您可以使用xdotool 安装xdotool它。如果两次击键之间的延迟应该一致,就这么简单:

xdotool type --delay 100 something

这种类型something与延迟100每个按键之间的毫秒。


如果两次击键之间的延迟应该是随机的,比如说从100到300毫秒,则情况会变得更加复杂:

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

这个for循环经过保存在变量字符串的每一个字母text,或者印刷key <letter>key space在一个空格,接着的情况下,sleep 0.和(1和3之间的随机数xdotoolsleep作为秒数解释)。然后,将循环的整个输出传递给xdotool,以打印字母,并且字母之间具有随机延迟。如果要更改延迟,只需更改部分,即上限和下限-为0.2到0.5秒即可。(RANDOM%x)+yyx-1+y(RANDOM%4)+2

请注意,这种方法不会打印文本,而是完全像用户那样键入文本,从而合成单个按键。结果,文本被输入到当前聚焦的窗口中。如果更改焦点,则文本的部分将在新聚焦的窗口中键入,这可能是您想要的,也可能不是。无论哪种情况,都请看这里的其他答案,所有这些都很棒!


24

在阅读@dessert的答案后,我尝试了xdotool,但由于某种原因无法使其正常工作。所以我想出了这个:

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

将您的文本输入上述代码,它将像键入的内容一样打印。您也可以通过替换sleep 0.1为来添加随机性sleep 0.$((RANDOM%3))

带有假错字的扩展版

这个版本会不时引入假错字并加以纠正:

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

我想我会改为:(必要时用换行符和好的缩进while IFS= read -r line; do for (( i = 0; i < ${#line}; i++ )); do sleep 0.1; printf "%s" "${line:i:1}"; done; echo; done代替;)。在IFS= read -rprintf "%s"确保空格和特殊字符没有任何差别。而且grep不需要在每一行上将其拆分为字符-只需for在该行中的每个字符上循环即可。
Digital Trauma '18

18

您提到了字符之间的一致延迟,但是如果您真的希望它看起来像被键入的字符,则时间安排就不会完全一致。为此,您可以使用script命令记录自己的键入内容并使用以下命令进行播放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停止记录。

-t参数传递给script指令还会生成时序信息,我已将其重定向到script.timing文件。我已经sed d作为命令传递给了,script因为这只是吸收输入(并且记录击键)而没有副作用的一种方式。

如果您也想执行所有tput/ reset内容,则可能需要script为每行做一个记录,然后将它们与tput/ reset命令交错插入以进行回放。


11

另一种可能性是使用Demo Magic,或者更确切地说,仅使用此脚本集合的打印功能,这基本上等于

#!/bin/bash

. ./demo-magic.sh -w2

p "this will look as if typed"

在幕后,它使用pv,当然您也可以使用pv直接获得所需的效果,基本形式如下所示:

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

1
pv的用法非常好。
塞巴斯蒂安·史塔克

3
无需管道echo传输到pv,仅pv -qL20 <<< "Hello world"在您的外壳支持herestrings时使用。
dr01

8

根据我的昵称,我可以提供另一种解决方案:

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

看起来很奇怪,不是吗?

  • -MTime::HiRes=usleepusleepTime::HiRes模块中导入功能(微秒睡眠),因为通常sleep只接受整数秒。
  • -F''将给定的输入拆分为字符(定界符为空''),并将字符放入数组中@F
  • BEGIN {$|=1} 禁用输出缓冲,以便立即打印每个字符。
  • for (@F) { print; usleep(100_000+rand(200_000)) } 只是遍历角色
  • 在Perl中使用下划线是一种常见的使用数千种分隔符的方法。Perl只是忽略了它们,因此我们可以例如编写1_000(== 1000),或者即使1_0_00我们认为这样更易于阅读。
  • rand() 返回一个介于0和给定参数之间的随机数,因此它们的睡眠时间在100,000到299,999微秒(0.1-0.3秒)之间。

出于好奇:是rand()将0传回参数(在您的示例中为100k至300​​k)还是参数之间(在您的示例中为100k + 1至300k-1)返回数字?
甜点

1
它返回间隔中的数字[0,200k),即包括0但不包括200k。此处记录了确切的行为:“返回大于或等于0且小于EXPR值的随机小数。(EXPR应该为正。)”
PerlDuck

1
如果没有-a和-n,则无法使用
rrauenza

@rrauenza 从Perl 5.20开始就可以了。-F暗示-a-a暗示-n
PerlDuck

好的,我使用的是5.16,这是在CentOS7上使用的。
rrauenza

6

不依赖x11或其他任何工具的可能可行的工具是asciicinema。它记录了您在终端机中所做的所有事情,并让您像回放屏幕截图一样重放它,仅此而已是纯粹基于ASCII的!您可能必须暂时禁用您的提示,以使其在外观上完全干净。正如其他人指出的那样,添加一致的延迟看起来并不自然,而自己键入它可能是您可以实现的最自然的外观之一。

记录完文字后,您可以执行以下操作:

$ asciinema play [your recording].cast; cmatrix

6

令我惊讶的是,现在还没有人提到,但是您可以使用库存工具和循环来完成此操作:

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

它只是逐个字符地循环输入,并在每次输入后延迟输出。唯一棘手的一点是,您必须将IFS设置为空字符串,以便bash不会尝试分割空间。

该解决方案非常简单,因此可以在字符,错别字之间添加可变的延迟,而这非常容易。

编辑(感谢@dessert):如果您想要一个更自然的界面,可以改而做

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

这将允许您将函数调用为,typeit foo bar而不是typeit 'foo bar'。请注意,如果没有引号,则参数会受到bash的单词拆分的影响,因此例如typeit foo<space><space>barprint foo<space>bar。要保留空格,请使用引号。


一个很好的建议,尽管应该注意分词是适用的。例如,typeit foo<space>bar将导致foo bar,而typeit foo<space><space>bar导致foo bar。您必须引用它以确保它是逐字记录。@dessert随时建议进行编辑。我可以自己做,但是我想给你机会来获得荣誉。
whereswalden

+1教我有关read -n1(+1 read -k1)的信息
塞巴斯蒂安·斯塔克

5

首先,正如其他人指出的那样,“看起来好像正在打字,字符之间始终有延迟...”有点矛盾。输入的内容不会产生一致的延迟。当您看到延迟产生的不一致的东西时,您会感到不寒而栗。“我的电脑被抢走了!!!!!?”

无论如何...

我必须大声疾呼expect,大多数Linux发行版都应该提供该功能。我知道老派,但是-假设它已安装-几乎不会再简单了:

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

从手册页:

-h标志强制输出(以某种方式)像实际键入的人类一样发送。人物之间出现类似人的延迟。(该算法基于Weibull分布,并进行了修改以适合该特定应用。)此输出由变量“ send_human”的值控制。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.