/bin/sh
나는 먼저 일부 CVS 저장소의 로컬 복사본을 로 업데이트한 rsync
다음 내 컴퓨터에서 체크아웃된 버전을 업데이트하는 이와 같은 스크립트(OpenBSD에 작성)를 가지고 있습니다 . 이 스크립트는 OpenBSD를 통해 루트로 실행됩니다.doas
( sudo
OpenBSD의 "동등"):
#!/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
중단하지 않고 나중에 스크립트의 두 번째 부분(저장소에서 업데이트된 내용에 따라 항상 실행하고 싶지는 않을 수도 있음)을 건너 뛰기 위해 키를 누를 수 있다는 것입니다 .su
rsync
이것은 작동하지 않으며 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
실험을 통해 이 도구가 다른 도구(예: ping
dodx)처럼 작동하고 호출하는 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
실행합니다 .wait
rsync
그런 다음 trap '' SIGINT SIGTERM EXIT
.