지연된 종료 스크립트

지연된 종료 스크립트

/bin/sh나는 먼저 일부 CVS 저장소의 로컬 복사본을 로 업데이트한 rsync다음 내 컴퓨터에서 체크아웃된 버전을 업데이트하는 이와 같은 스크립트(OpenBSD에 작성)를 가지고 있습니다 . 이 스크립트는 OpenBSD를 통해 루트로 실행됩니다.doas( sudoOpenBSD의 "동등"):

#!/bin/sh

int_handler () {
    echo 'Wait...' >&2
    quit=1
}

quit=0
trap int_handler INT

for r in CVSROOT src xenocara ports; do
    su cvsuser -c 'rsync --archive --itemize-changes --delete --omit-dir-times \
        "rsync://anoncvs.eu.openbsd.org/OpenBSD-cvs/$r/" \
        "/extra/cvs/$r/"'
done

trap - INT

[ "$quit" -eq 1 ] && exit

# rest of script updates checked-out CVS repositories from local copy

아이디어는 루프가 실행되는 Ctrl+C동안 루프와 해당 프로세스를 rsync중단하지 않고 나중에 스크립트의 두 번째 부분(저장소에서 업데이트된 내용에 따라 항상 실행하고 싶지는 않을 수도 있음)을 건너 뛰기 위해 키를 누를 수 있다는 것입니다 .sursync

이것은 작동하지 않으며 rsync프로세스는 신호를 수신하고 종료됩니다(그리고 for루프의 다음 반복이 계속됩니다). rsync다음과 같이 말합니다( Ctrl+C스크립트가 종료될 때까지 여러 번 누를 때).

^Crsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at rsync.c(642) [generator=3.1.3]
rsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at io.c(504) [receiver=3.1.3]
Wait...
rsync: [receiver] write error: Broken pipe (32)
rsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at io.c(1633) [sender=3.1.3]
^Crsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at rsync.c(642) [generator=3.1.3]rsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at io.c(504) [receiver=3.1.3
]

rsync: [receiver] write error: Broken pipe (32)
Wait...
^Crsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at rsync.c(642) [generator=3.1.3]
rsync error: received SIGUSR1 (code 19) at main.c(1440) [receiver=3.1.3]
Wait...

"라는 질문에 대한 답변에 따르면ctrl+c /not/ while 루프를 중단시키는 방법은 무엇입니까?", 적어도 실행 중에는 bash다음을 수행할 수 있어야 합니다.소홀히 하다signal INT, 이는 하위 프로세스도 신호를 무시하게 만듭니다. 이상적이지는 않지만 시도해 볼 수 있어 기쁩니다.

따라서 두 번째 시도는 다음과 같습니다.

#!/usr/local/bin/bash

trap '' INT

for r in CVSROOT src xenocara ports; do
    su cvsuser -c 'rsync --archive --itemize-changes --delete --omit-dir-times \
        "rsync://anoncvs.eu.openbsd.org/OpenBSD-cvs/$r/" \
        "/extra/cvs/$r/"'
done

trap - INT

read -p 'Press enter or interrupt...' junk

# rest of script updates checked-out CVS repositories from local copy

이를 통해 rsync프로세스가 중단될 수도 있습니다 Ctrl+C.

글쎄, 아마도 su신호 마스크를 재설정하는 이유일까요? 세 번째 시도:

#!/usr/local/bin/bash

trap '' INT

for r in CVSROOT src xenocara ports; do
    su -s /usr/local/bin/bash cvsuser -c \
        'trap "" INT; rsync --archive --itemize-changes --delete --omit-dir-times \
        "rsync://anoncvs.eu.openbsd.org/OpenBSD-cvs/$r/" \
        "/extra/cvs/$r/"'
done

trap - INT

read -p 'Press enter or interrupt...' junk

# rest of script updates checked-out CVS repositories from local copy

아니요, 기쁨이 없습니다. 프로세스 rsync는 다음과 같습니다.아직같은 방식으로 중단 가능합니다.

내가 (처음에) 의도한 대로 스크립트가 작동하도록 하는 방법이 있습니까?

bash내 컴퓨터의 버전은 4.4.23입니다. 이는 POSIX 모드에서 실행되는 OpenBSD의 기본 변형 /bin/sh입니다 .pdksh

답변1

그렇다면 실제로 인터럽트를 원하지 않는데 터미널 수준에서 인터럽트를 어떻게 비활성화합니까 stty intr ""? 이렇게 하면 입력 버퍼에 일반 문자로 나타나며 ^C거기에서 읽을 수 있습니다.

이것은 Linux에서 저에게 효과적입니다.

#!/bin/bash

t=$(stty -g)
stty intr ""
echo please hold
sleep 5
stty "$t"

# short timeout, just check if there was any input earlier
read -n1 -t0.1 a
[[ "$a" = $'\003' ]] && echo "you hit ^C"

(OpenBSD에서는 테스트할 수 없었지만 터미널 설정은 표준이라고 가정합니다.)

답변2

rsync시작 시 SIGINT가 무시되더라도 핸들러는 SIGINT에 설치됩니다. 따라서 rsync를 중지하고 싶지 않다면 ^C(패닉 버튼이 작동하지 않기 때문에 스스로 수행하는 것을 싫어합니다) SIGINT가 rsync.

따라서 터미널 드라이버에게 SIGINT를 포그라운드 프로세스 그룹으로 보내지 말라고 지시하십시오 ^C.@ikkachu의 답변rsync, 또는 포그라운드 프로세스 그룹 외부에 명령을 실행하는 프로세스를 배치합니다 .

신호 처리와 관련된 모든 것과 마찬가지로 이것이 작동하는지 여부는 셸마다(때로는 버전마다) 다릅니다.

가장 좋은 방법은 작동한다고 알고 있는 알려진 구현을 고수하고 몇 년 후에도 새 버전에서도 작동하기를 바라는 것입니다. 좋다 zsh:

#! /usr/bin/env zsh

# enable job control (run pipelines in different process groups)
set -o monitor

interrupted() false
trap 'echo Wait...; interrupted() true' INT

# run in background so it won't get the SIGINT upon ^C
# as it won't be in the foreground process group. Note that with
# version 5.1.1 at least, running it without & will also run in
# in a new process group, and not make it the foreground process
# group of the terminal as the shell is not interactive. However
# the trap would be run asynchronously.
rsync... &

while (($#jobstates)) {wait}

interrupted && exit

echo rest of the script

답변3

Linux에서는 이 명령을 사용하여 setsid별도의 세션에서 rsync를 실행할 수 있습니다. 시스템 호출이 존재하지만 OpenBSD에는 이를 명령으로 포함하지 않는 것 같습니다. C 버전의 명령을 직접 작성하거나 가능한 경우 Perl을 사용해 볼 수 있습니다.

#!/usr/bin/perl
use POSIX qw(setsid);
POSIX::setsid();
exec @ARGV;

스크립트 이름을 지정하면 mysetsid해당 이름을 명령의 접두사로 사용할 수 있습니다 su. 예를 들어 ./mysetsid su cvsuser ...스크립트의 첫 번째 버전에서 사용할 수 있습니다. 핸들러 기능은 현재 rsync 명령이 완료된 후에만 인터럽트를 볼 수 있습니다.

답변4

배쉬 구현

rsync실험을 통해 이 도구가 다른 도구(예: pingdodx)처럼 작동하고 호출하는 Bash 상위 도구로부터 신호를 상속받지 않는다는 것이 분명해졌습니다 .

따라서 약간의 창의력을 발휘하여 다음을 수행해야 합니다.

$ cat rsync.bash
#!/bin/sh

 set -m
 trap '' SIGINT SIGTERM EXIT
 rsync -avz LargeTestFile.500M [email protected]:/tmp/. &
 wait

 echo FIN

이제 실행하면 다음과 같습니다.

$ ./rsync.bash
X11 forwarding request failed
building file list ... done
LargeTestFile.500M
^C^C^C^C^C^C^C^C^C^C
sent 509984 bytes  received 42 bytes  92732.00 bytes/sec
total size is 524288000  speedup is 1027.96
FIN

파일이 완전히 전송되었음을 확인할 수 있습니다.

$ ll -h | grep Large
-rw-------. 1  501 games 500M Jul  9 21:44 LargeTestFile.500M

어떻게 작동하나요?

여기서의 비결은 Bash를 통해 set -m백그라운드 작업에 대한 작업 제어를 비활성화하도록 지시하는 것입니다. 그런 다음 마지막 실행 명령이 완료될 때까지 기다리는 명령을 백그라운드에서 rsync실행합니다 .waitrsync

그런 다음 trap '' SIGINT SIGTERM EXIT.

인용하다

관련 정보