쉘 스크립트에서 타임 아웃 15 foo=`cat` 타임 아웃을 추가하고

나는이 쉘 스크립트 있어 표준 입력에서 읽기 . 드문 경우이지만 입력을 제공 할 준비가 된 사람이 없으며 스크립트가 시간 초과 되어야합니다 . 시간 초과의 경우 스크립트는 정리 코드를 실행해야합니다. 가장 좋은 방법은 무엇입니까?

이 스크립트는 C 컴파일러가없는 20 세기 유닉스 시스템과 busybox를 실행하는 임베디드 장치를 포함하여 이식성이 뛰어나므로 Perl, bash, 컴파일 된 언어 및 전체 POSIX.2를 신뢰할 수 없습니다. 특히 $PPID, read -t완벽 POSIX 호환 트랩을 사용할 수 없습니다. 임시 파일에 쓰는 것도 제외됩니다. 모든 파일 시스템이 읽기 전용으로 마운트 된 경우에도 스크립트가 실행될 수 있습니다.

일을 더 어렵게 만들기 위해 시간이 초과되지 않을 때 스크립트가 합리적으로 빠르기 를 원합니다 . 특히 포크와 exec가 특히 낮은 Windows (주로 Cygwin)에서 스크립트를 사용하므로 최소한의 사용을 유지하고 싶습니다.

간단히 말해서, 나는

trap cleanup 1 2 3 15
foo=`cat`

타임 아웃을 추가하고 싶습니다. 나는 대체 할 수 없습니다 catread내장한다. 시간 초과의 경우 cleanup함수 를 실행하고 싶습니다 .


배경 :이 스크립트는 일부 8 비트 문자를 인쇄하고 커서 위치 전후를 비교하여 터미널의 인코딩을 추측합니다. 스크립트의 시작은 stdout이 지원되는 터미널에 연결되어 있는지 테스트하지만 때로는 환경이 거짓말하는 경우가 있습니다 (예 :로 호출하더라도 plink설정TERM=xtermTERM=dumb ). 스크립트의 관련 부분은 다음과 같습니다.

text='Éé'  # UTF-8; shows up as Ãé on a latin1 terminal
csi='␛['; dsr_cpr="${csi}6n"; dsr_ok="${csi}5n"  # ␛ is an escape character
stty_save=`stty -g`
cleanup () { stty "$stty_save"; }
trap 'cleanup; exit 120' 0 1 2 3 15     # cleanup code
stty eol 0 eof n -echo                # Input will end with `0n`
# echo-n is a function that outputs its argument without a newline
echo-n "$dsr_cpr$dsr_ok"              # Ask the terminal to report the cursor position
initial_report=`tr -dc \;0123456789`  # Expect ␛[42;10R␛[0n for y=42,x=10
echo-n "$text$dsr_cpr$dsr_ok"
final_report=`tr -dc \;0123456789`
cleanup
# Compute and return initial_x - final_x

tr2 초 후에도 입력을 읽지 않으면 종료되고 스크립트가 cleanup함수를 실행하도록 스크립트를 수정하려면 어떻게 해야 합니까?



답변

이건 어때?

foo=`{ { cat 1>&3; kill 0; } | { sleep 2; kill 0; } } 3>&1`

즉, 출력 생성 명령과 sleep동일한 프로세스 그룹에서 프로세스 그룹을 실행하십시오 . 어떤 명령이 먼저 반환 되더라도 전체 프로세스 그룹이 종료됩니다.

누구나 궁금해 할 것입니다. 예, 파이프가 사용되지 않습니다. 리디렉션을 사용하여 무시됩니다. 이것의 유일한 목적은 쉘이 동일한 프로세스 그룹에서 두 프로세스를 실행하도록하는 것입니다.


Gilles가 그의 의견에서 지적한 것처럼 스크립트 프로세스는 두 개의 하위 프로세스와 함께 종료되므로 쉘 스크립트에서는 작동하지 않습니다.

별도의 프로세스 그룹에서 명령을 강제 실행하는 방법 ¹은 새로운 대화식 쉘을 시작하는 것입니다.

#!/bin/sh
foo=`sh -ic '{ cat 1>&3; kill 0; } | { sleep 2; kill 0; }' 3>&1 2>/dev/null`
[ -n "$foo" ] && echo got: "$foo" || echo timeouted

그러나 이것에주의가있을 수 있습니다 (예 : stdin이 tty가 아닌 경우?). 대화식 쉘이 종료 될 때 “종료 됨”메시지를 제거하기 위해 stderr 리디렉션이 있습니다.

테스트 zsh, bashdash. 하지만 옛날 사람들은 어떻습니까?

B98 은 Mac OS X, GNU bash 3.2.57 또는 대시가있는 Linux에서 작동하는 다음 변경을 제안합니다.

foo=`sh -ic 'exec 3>&1 2>/dev/null; { cat 1>&3; kill 0; } | { sleep 2; kill 0; }'`


1. setsid비표준 인 것 이외의 것.


답변

me=$$
(sleep 2; kill $me >/dev/null 2>&1) & nuker=$!
# do whatever
kill $nuker >/dev/null 2>&1

이미 15 (트래픽이 없는 경우 전송 SIGTERM되는 숫자 버전)를 트래핑 kill중이므로 이미 가야합니다. 즉, POSIX 이전을 살펴보면 쉘 기능이 존재하지 않을 수도 있습니다 (System V의 쉘에서 제공됨).


답변

7.0 버전의 coretuils에 시간 초과 명령이 포함되어 있지만이를 포함하지 않는 일부 환경을 언급했습니다. 다행히 pixelbeat.org 에는 타임 아웃 스크립트가 작성되었습니다 sh.

나는 여러 번 전에 그것을 사용했으며 아주 잘 작동합니다.

http://www.pixelbeat.org/scripts/timeout ( 참고 : 아래 스크립트는 pixelbeat.org의 스크립트에서 약간 수정되었습니다.이 답변 아래의 주석을 참조하십시오.)

#!/bin/sh

# Execute a command with a timeout

# Author:
#    http://www.pixelbeat.org/
# Notes:
#    Note there is a timeout command packaged with coreutils since v7.0
#    If the timeout occurs the exit status is 124.
#    There is an asynchronous (and buggy) equivalent of this
#    script packaged with bash (under /usr/share/doc/ in my distro),
#    which I only noticed after writing this.
#    I noticed later again that there is a C equivalent of this packaged
#    with satan by Wietse Venema, and copied to forensics by Dan Farmer.
# Changes:
#    V1.0, Nov  3 2006, Initial release
#    V1.1, Nov 20 2007, Brad Greenlee <brad@footle.org>
#                       Make more portable by using the 'CHLD'
#                       signal spec rather than 17.
#    V1.3, Oct 29 2009, Ján Sáreník <jasan@x31.com>
#                       Even though this runs under dash,ksh etc.
#                       it doesn't actually timeout. So enforce bash for now.
#                       Also change exit on timeout from 128 to 124
#                       to match coreutils.
#    V2.0, Oct 30 2009, Ján Sáreník <jasan@x31.com>
#                       Rewritten to cover compatibility with other
#                       Bourne shell implementations (pdksh, dash)

if [ "$#" -lt "2" ]; then
    echo "Usage:   `basename $0` timeout_in_seconds command" >&2
    echo "Example: `basename $0` 2 sleep 3 || echo timeout" >&2
    exit 1
fi

cleanup()
{
    trap - ALRM               #reset handler to default
    kill -ALRM $a 2>/dev/null #stop timer subshell if running
    kill $! 2>/dev/null &&    #kill last job
      exit 124                #exit with 124 if it was running
}

watchit()
{
    trap "cleanup" ALRM
    sleep $1& wait
    kill -ALRM $$
}

watchit $1& a=$!         #start the timeout
shift                    #first param was timeout for sleep
trap "cleanup" ALRM INT  #cleanup after timeout
"$@" < /dev/tty & wait $!; RET=$?    #start the job wait for it and save its return value
kill -ALRM $a            #send ALRM signal to watchit
wait $a                  #wait for watchit to finish cleanup
exit $RET                #return the value

답변

이것을 위해 NC를 사용하는 것은 어떻습니까?

처럼;

   $ nc -l 0 2345 | cat &  # output come from here
   $ nc -w 5 0 2345   # input come from here and times out after 5 sec

또는 단일 명령 행으로 롤업되었습니다.

   $ foo=`nc -l 0 2222 | nc -w 5 0 2222`

마지막 버전은 Linux 시스템에서 테스트 할 때 실제로 stange가 작동하지만 실제로는 모든 시스템에서 작동해야하며 출력 리디렉션의 변형이 없다면 이식성이 해결 될 수 있습니다. 여기서 이점은 백그라운드 프로세스가 포함되지 않는다는 것입니다.


답변

자체 프로세스 그룹에서 파이프 라인을 실행하는 또 다른 방법 sh -c '....'script명령을 사용하여 의사 터미널에서 실행하는 것입니다 ( setsid함수 적으로 함수를 적용 함 ).

#!/bin/sh
stty -echo -onlcr
# GNU script
foo=`script -q -c 'sh -c "{ cat 1>&3; kill 0; } | { sleep 5; kill 0; }" 3>&1 2>/dev/null' /dev/null`
# FreeBSD script
#foo=`script -q /dev/null sh -c '{ cat 1>&3; kill 0; } | { sleep 5; kill 0; }' 3>&1 2>/dev/null`
stty echo onlcr
echo "foo: $foo"


# alternative without: stty -echo -onlcr
# cr=`printf '\r'`
# foo=`script -q -c 'sh -c "{ { cat 1>&3; kill 0; } | { sleep 5; kill 0; } } 3>&1 2>/dev/null"' /dev/null | sed -e "s/${cr}$//" -ne 'p;N'`  # GNU
# foo=`script -q /dev/null sh -c '{ { cat 1>&3; kill 0; } | { sleep 5; kill 0; } } 3>&1 2>/dev/null' | sed -e "s/${cr}$//" -ne 'p;N'`  # FreeBSD
# echo "foo: $foo"

답변

https://unix.stackexchange.com/a/18711 의 답변 은 매우 좋습니다.

비슷한 결과를 얻고 싶었지만 기존 쉘 함수를 호출하려고했기 때문에 명시 적으로 쉘을 다시 호출하지 않아도되었습니다.

bash를 사용하면 다음을 수행 할 수 있습니다.

eval 'set -m ; ( ... ) ; '"$(set +o)"

따라서 이미 쉘 기능이 있다고 가정하십시오 f.

f() { date ; kill 0 ; }

echo Before
eval 'set -m ; ( f ) ; '"$(set +o)"
echo After

이것을 실행하면 다음을 볼 수 있습니다.

$ sh /tmp/foo.sh
Before
Mon 14 Mar 2016 17:22:41 PDT
/tmp/foo.sh: line 4: 17763 Terminated: 15          ( f )
After

답변

timeout_handler () { echo Timeout. goodbye.;  exit 1; }
trap timeout_handler ALRM
( sleep 60; kill -ALRM $$ ) &