2017-01-24 12 views
3

複数のライブデータストリームの出力を複数の行にまたがって表示する関数が必要です。理想的な結果は、このような端末に出力します:複数の変数の 'live update'を表示するループスルーエコー

Measurement 1: #### 
Measurement 2: #### 

どこ#### 1秒間隔で更新が、それが画面に表示されているとして、テキストの残りの部分はとどまります。

この問題は1行の出力で解決されましたが、複数の行に拡張する方法を理解できません。これが唯一のループの各反復の後、端末上の出力の新しいブロックを作成し、端末上の二行目の上に書き込むには、連続出力を引き起こし

function doit { 
    while :; 
    do 
    i=$(current reading from sensor 1) 
    j=$(current reading from sensor 2) 
    echo -ne "Measurement 1: $i\nMeasurement 2: $j"'\r'; 
    sleep 1; 
    done 
} 

は、ここで私がこれまで持っているものです。

別の解決策があったhereを提示:

function doit { 
    while :; 
    do 
    i=$(current reading from sensor 1) 
    j=$(current reading from sensor 2) 
    echo -ne "Measurement 1: $i\nMeasurement 2: $j"'\r'; 
    sleep 1; 
    tput cuu1 
    tput el 
    tput cuu1 
    tput el 
    done 
} 

これは近いですが、画面のオンとオフテキストフラッシュのブロックとして不快なビジュアルを作成し、各ループの後に端末に表示されるすべてのテキストをクリアします。これは、誰かが読書を注意深く監視できるようにする必要があります。これを5行以上の出力に拡大する必要があります。ご意見ありがとうございました。また、いくつかのソリューションを見て楽しみにしています!

答えて

0

画面上に表示する必要がある以外のものがない限り、各サイクルをクリアすることができます。この場合、echo文には-nは必要ありません。 tputコマンドも必要ありません。

#!/bin/bash 
function doit { 
    while :; 
    do 
    i=$(current reading from sensor 1) 
    j=$(current reading from sensor 2) 
    clear 
    echo -e "Measurement 1: $i\nMeasurement 2: $j"'\r'; 
    sleep 1; 
    done 
} 
0

だけ保存すると、我々は、画面の下部にスクロールし、出力の原因に接近しすぎている場合tput sctput rc休憩でカーソル位置を復元する直感的なアプローチ。

trying to figure out what line we're currently onの代わりに、tput cuu nで印刷した行数だけもう一度カーソルを上に移動できます。この後、画面の最後までtput edで消去します。

センサーを読み取るのに時間がかかる場合、これはちらつきます(sleep 0.1とシミュレートされています)ので、新しいものを印刷する直前に行を消去しますが、センサーを読み取った後に行を消去します。これは最初に発生してはならないので、clearlinesが呼び出されるべきであることを示す最初の印刷後にフラグnotfirstを設定します。

すべてのすべて:

#!/bin/bash 

clearlines() { 
    local nlines=$1 
    tput cuu $nlines 
    tput ed 
} 

display() { 
    while true; do 
     # Get "sensor measurements" 
     sleep 0.1 # Simulate lag 
     local vars=($RANDOM $RANDOM $RANDOM $RANDOM $RANDOM) 

     # If this isn't the first output, clear the previous one 
     if [[ -n $notfirst ]]; then 
      clearlines ${#vars[@]} 
     fi 

     # Loop over "measurements" to print 
     local var 
     local i=0 
     for var in "${vars[@]}"; do 
      printf 'Measurement %d: %d\n' $((++i)) "${var}" 
     done 
     sleep 1 

     # Set flag 
     local notfirst=1 
    done 
} 

display 
+0

これは、あなたの2番目のアプローチで説明しているちらつきを引き起こす可能性があることを認識しています。私のためのものはありませんが、それは端末エミュレータによって異なる場合があります。 –

+0

$ RANDOMの時間が非常に短いので、ちらつきはありません。実際に通信速度の遅い通信線やネットワークを介して測定値を返す外部デバイスに実際にアクセスしている場合は、新しいデータを取得する前にブランクが目立つように点滅する可能性があります。また、 'tput cuu $ i'は' i''tput cuu1'sを実行するのと同じですが、少し速いです。 'tput cuu $ iを使うことができます。 tput ed'を使って '$ i'行を上に移動し、(各行を順に空白にする代わりに)画面の最後までクリアしますが、私はまだ私のバージョンを好みます。 – rici

+0

@rici良い点、私は更新します。 (そして私はあなたの解決策も気に入っています;)) –

1

ここでは、通常、何かを消すことはありませんが、場合に役立ちます各行の末尾に削除シーケンスを送信することにより、画面を点滅回避しようとバージョンが(です前の行が長くなった)。私は$RANDOMを使用して「測定を取得する」アクションを表しました。それは数字なので、%5dフォーマットを使用して、各サイクルで数字が同様に整列されるようにします(表示アーチファクトの回避にも役立ちます)。

function doit { 
    local up2=$(tput cuu 2)$(tput cr) up= 
    local endl=$(tput el)$'\n' 
    while :; do 
    local i=$RANDOM 
    local j=$RANDOM 
    printf "%sMeasurement 1: %5d%sMeasurement 2: %5d%s" "$up" "$i" "$endl" "$j" "$endl" 
    up=$up2 
    sleep 1; 
    done 
} 
0

すでに遅れがあなたのセンサデータを収集していますが、フリッカーの要因であってもよいし、他の表示おかしなを引き起こす可能性があるものは何でもによって引き起こされることが指摘されています。

ここでは最小限の形でこれを代替する方法を示します。

#!/usr/bin/env/ bash 

# This generates random sensor data and stores it in predictably named temp files, 
# after a short random delay. 
function gather() { 
    while sleep $(($RANDOM % 3)); do 
     printf '%2d\n' $(($RANDOM % 100)) > /tmp/t.$$.$1 
    done 
} 

main() { 

    # Background our data gatherers... 
    gather i & 
    gather j & 

    # Print a couple of blank lines, since our format starts with "up" 
    printf '\n\n' 

    # and loop. 
    while sleep 1; do 
     # gather our randomly generated sensor data... 
     i=$(< /tmp/t.$$.i) 
     j=$(< /tmp/t.$$.j) 
     # and print it. 
     printf "$fmt" "$i" "$j" 
    done 
} 

cleol=$(tput el) 
up2=$(tput cuu1; tput cuu1) 
fmt="${up2}Measurement 1: %d${cleol}\nMeasurement 2: %d${cleol}\n" 

main 

カーソル移動が楽しいです。 :-)

しかし、すべての点では、あなたの出力を書く責任があるループは非常に軽量です。それは静的テキストファイルからデータを取得し、印刷します。その後、繰り返します。すべての重い持ち上げは、バックグラウンドのgather()関数で行われます。この関数は、ディスプレイの更新頻度とは異なる頻度でテキストファイルを更新します。

書かれているように、「書き込み」が起きている間ではなく、データが書き込まれる前に、「読み込み」がファイルに当たる可能性のある競合状態があることに注意してください。それを最小限に抑えるために、あなたはこのようなものでレースを回避することができます:

function gather() { 
    while sleep 1; do 
     value=$(
      # This simulates the delay of a slow sensor read. 
      sleep $(($RANDOM % 3)) 
      printf '%2d\n' $(($RANDOM % 100)) 
     ) 
     printf '%d\n' "$value" > /tmp/t.$$.$1 
    done 
} 

これには、優れている、と「十分」であってもよいが、このような一時ファイルへの書き込みはアトミックファイルシステムの操作ではありません競合状態の可能性は依然として存在する。

以下のバリエーションでは、データの読み込み中に長い遅延をシミュレートしますが、読み込みコマンドの出力は、別の一時ファイルに移動し、センサ読み込みの完了時にシンボリックリンクします。

function gather() { 
    local n=0 item="${1//[^[:alnum:]]/}" 
    while :; do 
     old="$(readlink /tmp/t.$$.$item)" 
     (
      # This simulates the delay of a slow sensor read. 
      sleep $(($RANDOM % 3)) 
      printf '%d\n' $(($RANDOM % 100)) 
     ) > /tmp/t.$$.$item.$n 
     test -s /tmp/t.$$.$item.$n && 
      ln -sfn /tmp/t.$$.$item.$n /tmp/t.$$.$item && 
     test -f "$old" && 
      rm -f "$old" 
     ((n++)) 
     sleep 1 
    done 
} 

カッコ内のすべては、センサー出力を生成するために実行するものです。 $nの値が不足するのを恐れている場合(ただし、陽子減衰を恐れることはありません)、増分カウンタを使用する代わりに$RANDOMに切り替えることができます。

監視しているものは何でも、既存のモンタージュシステムを使用することを考えましたか?このような問題はすでに解決されているように思えます。 :-)


UPDATE ...

そして、私はよく私の時間を使用していないという理由だけで、ここに改善main()機能やスクリプトの残りの部分です:

function main() { 

    # Background our data gatherers... 
    local -a pids=() 
    for thing in "${things[@]}"; do 
     gather $thing & 
     pids+=($!); echo "Backgrounded $thing at $!" 
    done 
    trap "echo 'Quitting...'; kill $(printf '%s ' "${pids[@]}"); exit 0" 2 
    trap "echo 'Aborting...'; kill $(printf '%s ' "${pids[@]}"); exit 1" 1 3 15 

    # Wait for gatherers to have at least one round of data 
    sleep 2 

    # and loop. 
    local -A v=() 
    while sleep 1; do 
     # gather our randomly generated sensor data... 
     for thing in "${things[@]}"; do 
      v[$thing]=$(< /tmp/t.$$.$thing) 
     done 
     # and print it. 
     printf "$fmt" "${v[@]}" 
    done 
} 

things=(i j)    # the list of things to monitor 
cleol=$(tput el)    # you know what this is already 
up2=$(tput cuu1; tput cuu1) # this too 
fmt="${up2}Measurement 1: %d${cleol}\nMeasurement 2: %d${cleol}\n" 

main 

これは、モニターされた項目のリストを必要に応じてbash配列$things[]と背景に置きます。エラーをトラップし、適切に切断します。それがしない1つのことは、$fmtを配列のサイズに適合させることです。またはコーヒーを作る。

関連する問題