他の操作中にユーザー入力をキャプチャ

他の操作中にユーザー入力をキャプチャ

サブプロセス(下の「スリープ」を除く項目)の出力を前景コマンドループの出力と一致させる方法はありますか?たとえば、

while true
do
    echo "updating screen..."
    sleep 3 & # Replace with command which takes some time and updates the screen
    read -s -n 1 -p "Input: " input
    case "$input" in
    q)
        exit
    ;;
    f)
        echo 'foo'
    ;;
    esac
done

ベストアンサー1

シェルは何よりも多重処理を想定しています。 1つのプログラムが一度に端末を制御する必要があります。そうしないと、入力(および出力)が壊れます。端末から入力を受けたい「スリープ」プログラムを入れるとどうなりますか?キーボード入力はどこに送信されますか?子プロセス( 'sleep')または文でread

これにより、子プロセス(「スリープ」)が入力を受け取らないと仮定する必要があります。また、コマンドループ(「q」または「f」の処理)まで待つ必要があります。そしてサブプロセスが完了しました。シェルの仮定を解決するために、Pythonのようなシェルスクリプト以外のものを書くことをお勧めします。ただし、前述のように、Bourne(またはksh、bash、またはzsh)シェルでも実行できます。

#!/usr/bin/python
import os, select, subprocess, sys
devnull = open('/dev/null', 'a')
# this is the same as "sleep 3 </dev/null > pipefile &" but will handle
# processing output at the same time
p = subprocess.Popen(
    ['/bin/sh', '-c',
 'i=0; while [ $i -lt 30 ]; do echo $i; sleep 2; i=`expr $i + 1`; done' ],
    stdin=devnull, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
)
command_done = False
try:
    # endless loop until both input and output are done
    while True:
        inputs = []
        # only send input to our command loop
        if not command_done:
            sys.stdout.write('Input: ')
            sys.stdout.flush()
            inputs.append(sys.stdin)
        if p.returncode is None: # still not finished
            p.poll()
            inputs.append(p.stdout)
        outputs = []
        if not inputs and not outputs: # both are done
            break # exit while loop
        #print inputs, outputs
        r, w, x = select.select(inputs, outputs, [])
        #print 'r=', r, 'w=', w, 'x=', x
        # input from the user is ready
        for file in r:
            if file is sys.stdin:
                input = file.read(1)
                if input == 'q':
                    command_done = True
                    if p.returncode is None:
                        os.kill(p.pid, 15)
                elif input == 'f':
                    sys.stdout.write('foo\n')
            # the subprocess wants to write to the terminal too
            else:
                input = file.readline()
                sys.stdout.write(input)
finally:
    if p.poll():
        try:
            print
            os.kill(p.pid, 15)
        except OSError:
            pass

シェルスクリプトでこれを実行できますが、入力/出力が正しく統合されていません。

#!/bin/bash
mkfifo /tmp/mergedout.p
( i=0; while [ $i -lt 30 ]; do echo $i; sleep `expr 30 - $i`; i=`expr $i + 1`; done ) </dev/null >/tmp/mergedout.p 2>&1 &
pid=$!
exec 3</tmp/mergedout.p
done=false
trap 'rm /tmp/mergedout.p' 0
while [ -n "$pid" -a $done = false ]; do
    if [ $done = false ]; then
        echo -n "Input: "
        read -t 0.1 -s -r -n 1
        case $REPLY in
            q) done=true; if [ -n "$pid" ]; then kill $pid; fi;;
            f) echo foo;;
        esac
    fi
    if [ -n "$pid" ]; then
        kill -0 $pid 2>&-
        if [ $? -ne 0 ]; then
            echo "$pid terminated"
            wait $pid
            pid=""
            exec 3<&-
        else
            read -t 0.1 -u 3 -r
            echo "reading from fd3: X${REPLY}X $?"
            if [ -n "$REPLY" ]; then
                echo "$REPLY"
            fi
        fi
    fi
    sleep 0.5
done

私自身はPythonがよりきれいで「パーソナライズされています」ですが、ほとんどの場合どちらの方法でも実行できます。

おすすめ記事