web-dev-qa-db-ja.com

入力されているかのように端末でテキストを印刷する方法は?

.bashrcに追加した簡単なecho印刷物があります。

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

これにより、端末にメッセージが出力されますが、文字間で一貫した遅延が発生しているように見えます。

26

これはWaylandでは機能しません。 Ubuntu 17.10を使用していて、ログイン時にXorgを使用するように変更しなかった場合、このソリューションは適切ではありません。

xdotoolを使用できます Install xdotool そのため。キーストローク間の遅延がconsistentである場合、それはそれと同じくらい簡単です:

xdotool type --delay 100 something

これは、各キーストロークの間に100ミリ秒の遅延でsomethingを入力します。


キーストローク間の遅延がrandomである場合、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にパイプ処理され、ランダムな遅延の間に文字が出力されます。遅延を変更する場合は、(RANDOM%x)+y部分を変更します。yが下限で、x-1+yが上限です。0.2〜0.5秒の場合、(RANDOM%4)+2になります。

このアプローチはテキストをprintするのではなく、むしろtypeのようにユーザーが行うように、単一のキー入力を合成することに注意してください。結果として、テキストは現在フォーカスされているウィンドウに入力されます。テキストのフォーカス部分を変更すると、新しくフォーカスされたウィンドウにテキストが入力されますが、これは必要な場合とそうでない場合があります。いずれの場合も、ここで他の回答をご覧ください。それらはすべて素晴らしいです!

28
dessert

@dessertの答えを読んだ後、xdotoolを試しましたが、何らかの理由で動作しませんでした。だから私はこれを思いついた:

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

テキストを上記のコードにパイプすると、入力したとおりに印刷されます。 sleep 0.1sleep 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
24
Sebastian Stark

文字間の一貫した遅延に言及しますが、入力されたように見せたい場合、タイミングは完全に一貫していません。これを実現するには、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コマンド。

18
Digital Trauma

もう1つの可能性は、 デモマジック を使用することです。より正確には、このスクリプトコレクションの印刷機能を使用するだけです。

#!/bin/bash

. ./demo-magic.sh -w2

p "this will look as if typed"

ボンネットの下では、これは pv を使用します。もちろん、これを使用して目的の効果を直接取得することもできます。基本フォームは次のようになります。

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

私のニックネームに沿って、別のソリューションを提供できます。

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

変に見えますよね?

  • -MTime::HiRes=usleepTime::HiResモジュールから関数usleep(マイクロ秒スリープ)をインポートします。これは、通常の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秒)スリープします。
8
PerlDuck

動作する可能性のある別のツールは、x11などには依存しませんが、 asciicinema です。ターミナルで行うすべてを記録し、それをスクリーンキャプチャのように再生できるようにします。ただし、純粋に視覚的にきれいにするために、プロンプトを一時的に無効にする必要がある場合があります。他の人が指摘したように、一貫した遅延を追加することは自然に見えず、自分で入力することはあなたが達成できる最も自然な外観の一つかもしれません。

テキストを記録した後、次のようなことができます。

$ asciinema play [your recording].cast; cmatrix
6
rien333

誰もこれについて言及していないことに驚いていますが、ストックツールとループでこれを達成できます。

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

入力文字を1文字ずつループし、それぞれの後に遅延して出力します。唯一の注意点は、bashがスペースを分割しようとしないように、IFSを空の文字列に設定する必要があることです。

このソリューションは非常に単純なので、文字間での可変遅延、タイプミスなど、非常に簡単なものを追加します。

編集(ありがとう、@ dessert):もう少し自然なインターフェイスが必要な場合は、代わりに行うことができます

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

これにより、関数をtypeit foo barではなくtypeit 'foo bar'として呼び出すことができます。引用符がないと、引数はbashのWord分割の対象になるため、たとえばtypeit foo<space><space>barfoo<space>barを出力します。空白を保持するには、引用符を使用します。

6
whereswalden

最初に、「文字が入力されているように見え、文字間で一貫した遅延があります...」は、他の人が指摘しているように、少し矛盾しています。タイプされているものには、一貫した遅延がありません。一貫性のない遅延で生成されたものを見ると、悪寒を覚えます。 「何が私のコンピューターを乗っ取ったのでしょうか!!! ??!?」

とにかく...

ほとんどのLinuxディストリビューションで利用できるはずのexpectに叫ぶ必要があります。オールドスクール、私は知っていますが、インストールされていると仮定すると、それは簡単ではありません:

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

Manページから:

-hフラグは、実際に入力する人間のように(ある程度)出力を強制的に送信します。キャラクター間に人間のような遅延が現れます。 (アルゴリズムは、この特定のアプリケーションに合わせて修正されたワイブル分布に基づいています。)この出力は、変数「send_human」の値によって制御されます...

https://www.tcl.tk/man/expect5.31/expect.1.html を参照してください

5
Mike S