Bash 스크립트에서 시계 신호를 캡처할 수 있나요?

Bash 스크립트에서 시계 신호를 캡처할 수 있나요?

스크립트에서 5분 신호를 캡처할 수 있나요? 이런 상상을 해봅니다.

function dosomething {
   echo "It's been 5 minutes."
}

trap dosomething SIGNAL-EVERY-5-MINUTES

while true
do
    sleep 10
done

이 예는 의미가 없습니다. x번마다 무언가를 캡처하는 것이 가능한지 물어보세요.


편집하다:sleep@vinc17의 답변을 바탕으로 메인 셸의 명령이 신호에 의해 중단되지 않는다는 것을 알았습니다 USR1. 이것이 제가 원하는 것입니다.

#!/bin/bash
time=10

(
  set -e
  while true
  do
    sleep 30
    # Since this is run as a subshell (instead of an external command),
    # the parent pid is $$, not $PPID.
    kill -USR1 $$
  done
) &

finish() {
   kill $!
   echo "Closing."
   exit 0
}

changetime() {
   echo "Signal USR1 received."
   time=$(( $RANDOM % 8 + 1))
}

trap changetime USR1
trap finish SIGINT

while true
do
  echo "before sleep"
  sleep $time
  echo "after sleep"
done

산출,

before sleep
Signal USR1 received.
after sleep
before sleep
Signal USR1 received.
after sleep
before sleep
Signal USR1 received.
Closing.

편집 2:동일한 출력을 얻기 위해 위의 예를 편집했지만 어려움이 추가되었습니다. USR1트랩 기능이 수면 시간을 변경합니다. 이제 30초마다 1에서 8 사이에서 임의의 시간을 선택하세요. 따라서 sleep신호가 시간을 변경하면 기본 스크립트를 종료해야 합니다. 나는 이것이 말이 되지 않는다고 주장하지만 이것이 가능한지 알아야 합니다.

답변1

SIGALRM예를 들어 x번마다 쉘 스크립트에 신호를 보내고 해당 신호를 트랩하는 프로세스를 실행할 수 있습니다 . 이 프로세스는 다음을 수행하는 스크립트일 수 있습니다.

set -e
while true
do
  sleep 300
  kill -ALRM $PPID
done

기본 쉘 스크립트에 의해 시작된 경우.

기본 쉘 스크립트는 더 이상 필요하지 않을 때 프로세스를 종료해야 하며/또는 pid가 더 이상 존재하지 않을 때(그러나 경쟁 조건이 있을 때) 프로세스를 종료해야 합니다.

(수정됨)참고: 기본 쉘 스크립트가 이 명령을 사용하고 이것이 내장된 경우 신호와 잘못 상호 작용할 sleep수 있습니다 .ALRMsleepsleep(3)사용alarm(2). 이것sleep쉘 유틸리티에 대한 POSIX 설명또한 그 이유에 대해 다음과 같이 밝혔습니다.SIGALRM 신호에 의해 절전 모드가 중단되면 종료 상태는 0이 될 수 있습니다. 왜냐하면 이 유틸리티의 대부분의 구현은 요청된 완료 시간에 성공적으로 도달했음을 알리기 위해 이 신호의 도착에 의존하기 때문입니다. 따라서 이러한 구현에서는 이 경우와 성공적인 완료의 경우를 구별할 수 없습니다. 다른 구현에서는 요청된 시간이 만료될 때까지 신호를 포착하고 절전 모드로 돌아가거나 정상적인 신호 종료 절차를 제공할 수 있습니다."특정 구현 에서 발생할 수 있는 문제를 방지 SIGUSR1하려면 .SIGUSR2SIGALRM

다음은 서브쉘을 사용하는 예입니다. 동작을 더 쉽게 볼 수 있도록 5분 기간( sleep 300)을 5초 기간( sleep 5)으로 대체했습니다.

#!/bin/sh

{
  set -e
  while true
  do
    sleep 5
    # Since this is run as a subshell (instead of an external command),
    # the parent pid is $$, not $PPID.
    kill -USR1 $$
  done
} &

trap 'echo "Signal USR1 received."' USR1

while true
do
  date
  sleep 1
done

Ctrl-C를 사용하여 스크립트를 중단할 수 있습니다. 이런 일이 발생하면 서브셸이 종료되지 않지만 pid가 재사용되지 않으면 명령이 kill실패했기 때문에( kill: No such process) 일정 시간(여기서는 5초) 후에 서브셸이 자동으로 종료됩니다.

답변2

예. 가장 쉬운 방법은 원래 질문에 설명된 방식대로 수행하는 것입니다. 이건 단지 일부일 뿐이야무엇다음 5분 이내에 프로세스에 신호를 보내려면 다른 하위 프로세스를 생성해야 합니다. @vinc17 님의 책 한 페이지를 가져와서 간격도 5초로 단축했습니다.

trap 'ME=$$
     : $( (sleep 5
     ps -p $ME >/dev/null 2>&1 && { 
        echo hey >/dev/tty
        kill -USR1 $ME
     })&)
' USR1; kill -USR1 $$

이렇게 하면 좀비화를 요청하는 장기간 실행 프로세스가 필요하지 않습니다. 대신 매 간격마다 상위 PID에 대한 새로운 참조를 얻는 새로운 단일 목적 카운터가 있습니다. 또한 부모가 떠난 후 사망하는 경우 자녀가 스스로 일을 하려고 하지 않도록 작은 검사도 추가했습니다.

이를 실행하면 5초마다 다음을 얻을 수 있습니다.

hey
hey
hey
hey
hey
hey
hey
hey
hey
hey

이 예에서는 동시에 터미널을 일시 중지할 수도 있습니다. 그렇지 않으면 전체 프로세스를 백그라운드에 두려고 해도 아무 작업도 수행되지 않습니다. 이는 보존 때문입니다 /dev/tty. 전체 프로세스를 백그라운드에서 실행하려는 경우 파일을 선택하고, 그렇지 않으면 sttytty를 직접 호출하고 필요할 때 다시 실행하십시오.

답변3

USR1 트랩이 예상대로 실행되도록 으로 sleep $time교체 하십시오 . sleep $time & wait $!바라보다다른 질문에 답하다또는 설명을 보려면 bash 매뉴얼 페이지의 SIGNALS 장을 참조하세요.

내 시스템에서는 ALRM 신호가 USR1과 마찬가지로 작동하는 것 같습니다.

관련 정보