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

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

我有一份简单的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

这不适用于 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.上限– 0.2 到 0.5 秒为。xdotoolsleepxdotool(RANDOM%x)+yyx-1+y(RANDOM%4)+2

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

答案2

在阅读@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

答案3

您提到字符之间有一致的延迟,但如果您真的希望它看起来像是正在输入,则时间不会完全一致。 要实现这一点,您可以使用命令录制您自己的输入,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 即可停止录制。

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

如果您也想做所有的tput/reset事情,您可能需要script对每一行进行录音,然后播放它们,并与tput/reset命令交错。

答案4

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

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

看起来很奇怪,不是吗?

  • -MTime::HiRes=usleep从模块导入函数usleep(微秒睡眠),Time::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 秒)。

相关内容