如何在终端中打印文本就像正在输入一样?

Sim*_*ied 26 command-line bash echo

我有一个简单的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
Run Code Online (Sandbox Code Playgroud)

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

des*_*ert 28

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

您可以使用 xdotool 安装 xdotool为了那个原因。如果击键之间的延迟应该是一致的,就这么简单:

xdotool type --delay 100 something
Run Code Online (Sandbox Code Playgroud)

这种类型在每次击键之间something100几毫秒的延迟。


如果击键之间的延迟应该是随机的,比如说从 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 -
Run Code Online (Sandbox Code Playgroud)

这个for循环遍历保存在变量中的字符串的每个字母text,打印key <letter>或者key space在空格后跟sleep 0.一个 1 到 3 之间的随机数的情况下打印(xdotool'ssleep将数字解释为秒)。然后将循环的整个输出通过管道传送到xdotool,打印字母之间的随机延迟。如果您想更改延迟,只需更改部分,即下限和上限 – 0.2 到 0.5 秒将是。(RANDOM%x)+yyx-1+y(RANDOM%4)+2

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


Seb*_*ark 25

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

while read line
do
    grep -o . <<<$line | while read a
    do
        sleep 0.1
        echo -n "${a:- }"
    done
    echo
done
Run Code Online (Sandbox Code Playgroud)

将您的文本输入到上面的代码中,它会像打字一样打印出来。您还可以通过替换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
Run Code Online (Sandbox Code Playgroud)


Dig*_*uma 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 ...

$ 
Run Code Online (Sandbox Code Playgroud)

按 CTRL-D 停止录制。

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

如果你也想做所有的tput/reset东西,你可能想为你的script每一行录音,然后播放它们,与tput/reset命令交错。


god*_*lka 11

另一种可能性是使用Demo Magic,或者,更准确地说只是这个脚本集合的打印功能,基本上相当于

#!/bin/bash

. ./demo-magic.sh -w2

p "this will look as if typed"
Run Code Online (Sandbox Code Playgroud)

在引擎盖下,这使用pv,当然您也可以使用它来直接获得所需的效果,基本形式如下:

echo "this will look as if typed" | pv -qL 20
Run Code Online (Sandbox Code Playgroud)

  • 不需要通过管道将 `echo` 传送到 `pv`,如果你的 shell 支持 herestrings,只需使用 `pv -qL20 &lt;&lt;&lt; "Hello world"`。 (3认同)

Per*_*uck 8

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

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

看起来很奇怪,不是吗?

  • -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 秒)之间休眠。


whe*_*den 6

我很惊讶还没有人提到这一点,但是您可以使用库存工具和循环来完成此操作:

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

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

这个解决方案非常简单,因此在字符之间添加可变延迟、拼写错误等都非常容易。

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

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

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


rie*_*333 6

另一个可能工作的工具是asciicinema,它不依赖于 x11 或其他任何东西。它会记录您在终端中所做的一切,并让您像截屏一样重放它,只有这样它才完全基于 ASCII!您可能必须暂时禁用您的提示,但它是纯粹的视觉清洁。正如其他人指出的那样,添加一致的延迟看起来并不自然,而自己输入可能是您可以实现的最自然的外观之一。

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

$ asciinema play [your recording].cast; cmatrix
Run Code Online (Sandbox Code Playgroud)


Mik*_*e S 5

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

反正...

我必须对 大喊大叫expect,它应该在大多数 Linux 发行版上都可用。老派,我知道,但是 - 假设它已安装 - 它几乎不可能更简单:

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

从手册页:

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

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