나는쉘 스크립트그건표준 입력에서 읽기. 입력을 제공할 준비가 되어 있는 사람이 없는 드문 경우에는 스크립트가 다음을 수행해야 합니다.정지시키다. 시간이 초과되면 스크립트는 일부 정리 코드를 실행해야 합니다. 가장 좋은 접근 방식은 무엇입니까?
이 스크립트는 다음과 같아야 합니다.매우 휴대 가능20세기 유닉스 시스템과 C 컴파일러 없이 busybox를 실행하는 임베디드 장치를 포함하므로 Perl, bash, 컴파일된 언어 또는 심지어 전체 POSIX.2도 신뢰할 수 없습니다. 특히 POSIX $PPID
와 read -t
완전히 호환되는 트랩은 사용할 수 없습니다. 임시 파일에 쓰기도 다루지 않습니다. 모든 파일 시스템이 읽기 전용으로 마운트된 경우에도 스크립트가 실행될 수 있습니다.
더 어렵게 만들 뿐이죠. 대본이 의미가 있었으면 좋겠어요빠르게시간이 초과되지 않을 때. 특히 저는 Windows(주로 Cygwin)에서도 스크립트를 사용하는데, 여기서는 fork와 exec가 특히 낮은 수준이므로 그 사용을 최소한으로 유지하고 싶습니다.
요컨대 나는
trap cleanup 1 2 3 15
foo=`cat`
시간 초과를 추가하고 싶습니다. 내장형 cat
으로 교체 할 수 없습니다 . read
시간 초과가 발생하면 함수를 실행하고 싶습니다 cleanup
.
배경: 이 스크립트는 일부 8비트 문자를 인쇄하고 이전 커서 위치와 다음 커서 위치를 비교하여 터미널의 인코딩을 추측합니다. 스크립트의 시작 부분에서는 stdout이 지원되는 터미널에 연결되어 있는지 테스트하지만 때로는 환경이 거짓말을 합니다(예:plink
TERM=xterm
호출되더라도 설정됩니다.TERM=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
tr
2초 후에도 입력을 읽지 않으면 스크립트가 종료되고 함수 cleanup
가 실행되도록 스크립트를 수정하려면 어떻게 해야 합니까 ?
답변1
이것은 어떻습니까?
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
, bash
및 테스트를 사용하십시오 dash
. 하지만 오래된 것은 어떻습니까?
B98GNU bash 3.2.57을 사용하는 Mac OS X 또는 대시를 사용하는 Linux에는 다음 변경 사항을 권장합니다.
foo=`sh -ic 'exec 3>&1 2>/dev/null; { cat 1>&3; kill 0; } | { sleep 2; kill 0; }'`
–
1. setsid
겉보기에 비표준적인 상황을 제외하고.
답변2
me=$$
(sleep 2; kill $me >/dev/null 2>&1) & nuker=$!
# do whatever
kill $nuker >/dev/null 2>&1
SIGTERM
15개( 달리 명시하지 않는 한 숫자 버전이 전송됨 ) 를 캡처했으므로 kill
이제 잘 진행됩니다. 즉, POSIX 이전 버전을 보고 있는 경우 셸 기능도 존재하지 않을 수 있다는 점에 유의하세요(System V 셸에서 제공됨).
답변3
coretuils 버전 7.0에는 timeout 명령이 포함되어 있지만 일부 환경에는 이 명령이 없다고 언급하셨습니다. 다행스럽게도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 <[email protected]>
# Make more portable by using the 'CHLD'
# signal spec rather than 17.
# V1.3, Oct 29 2009, Ján Sáreník <[email protected]>
# 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 <[email protected]>
# 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
답변4
자체 프로세스 그룹에서 파이프를 실행하는 또 다른 방법은 sh -c '....'
의사 터미널 script
(함수가 암시적으로 적용되는 곳 setsid
)에서 run 명령을 사용하는 것입니다.
#!/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"