다른 작업을 수행하는 동안 사용자 입력 캡처

다른 작업을 수행하는 동안 사용자 입력 캡처

하위 프로세스(아래의 "sleep"을 제외한 항목)의 출력을 전경 명령 루프의 출력과 일치시키는 방법이 있습니까? 예를 들어:

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

쉘은 무엇보다도 다중 처리에 대해 가정합니다. 하나의 프로그램이 한 번에 터미널을 제어해야 합니다. 그렇지 않으면 입력(및 출력)이 깨질 것입니다. 터미널에서 입력을 받기를 원하는 "잠자기" 프로그램을 넣으면 어떻게 될까요? 키보드 입력은 어디로 전송되나요? 자식 프로세스('sleep') 또는 명령문으로 read?

이를 통해 하위 프로세스("sleep")가 입력을 받지 못한다고 가정해야 합니다. 또한 명령 루프("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이 더 깔끔하고 "맞춤형"이지만 대부분의 경우 어느 쪽이든 수행할 수 있습니다.

관련 정보