루프 동안 bash에 영향을 미치기 위해 시간 제한 전역 변수를 사용하는 방법은 무엇입니까?

루프 동안 bash에 영향을 미치기 위해 시간 제한 전역 변수를 사용하는 방법은 무엇입니까?

whilebash내 스크립트에는 처음과 5초 간격으로 서로 다른 작업을 수행하도록 되어 있는 루프가 있습니다 . 이전 루프가 완료되도록 허용합니다. 5초 간격은 함수에 의해 do_different설정된 전역 변수로 표시됩니다 heartbeat. 또 다른 문제는 일반 while루프가 알 수 없는 시간 내에 완료된다는 것입니다( RANDOM아래 스크립트에서 단순화됨). 사용은 cron옵션이 아니며 무작위 프로세스 타이밍도 아닙니다.

성공하지 못한 채 파이프 및 프로세스 대체를 사용해 보았습니다. 전체 스크립트를 리팩터링할 수 있습니다.

#!/bin/bash

function heartbeat {
    do_different=true
    while sleep 5s
    do
        do_different=true
    done
}

heartbeat &

while true
do
    if $do_different
    then
        echo 'Something different'
        do_different=false
        i=0
    else
        # process of random duration; not important
        r=$(( 1 + RANDOM % 3 ))
        sleep "${r}s"
        i=$((i + r))
        echo "$i"
    fi
done

답변1

첫째, 명확하지 않은 경우 문제의 스크립트는 heartbeat하위 프로세스에서 실행되므로 상위 셸 메모리의 셸 변수를 변경할 수 없기 때문에 실패합니다.

OP가 하려는 정신에 더 가까운 접근 방식은 다음과 같습니다.

#!/bin/bash

trap 'do_different=true' USR1

heartbeat() {
    while sleep 5
    do                  # If the parent process has gone away, the child should terminate.
        kill -USR1 "$$"  ||  exit
    done
}

heartbeat &
heartbeat_pid=$!
trap 'kill "$heartbeat_pid"' 0

do_different=true

while true
do
    if "$do_different"
    then
        echo 'Something different'
        do_different=false
        i=0
    else
        # process of random duration; not important
        r=$(( 1 + RANDOM % 3 ))
        sleep "$r"
        i=$((i + r))
        echo "$i"
    fi
done

수정된 프로세스는 heartbeatSIGUSR1 신호를 기본(부모) 셸 프로세스로 보냅니다. 이 신호와 SIGUSR2는 사용자/애플리케이션 사용을 위해 예약되어 있으며 시스템에서 생성되어서는 안 됩니다. 이 trap명령을 사용하면 쉘 스크립트가 신호를 캡처할 수 있습니다. 이 trap 'do_different=true' USR1명령은 SIGUSR1 신호(5초마다 도착)를 포착하고 do_different신호가 발생할 때 플래그를 설정하도록 쉘에 지시합니다.

heartbeat_pid분명히 자식 프로세스의 프로세스 ID(PID)입니다 heartbeat. 이 명령은 trap 'kill "$heartbeat_pid"' 0스크립트가 종료되었음을 나타내는 가짜 신호 0이 "수신"될 때 어떤 일이 발생하는지 정의합니다. Shell이 ​​문에 "떠난 후 집에가는 길에 식료품을 가져가는 것을 잊지 마세요"라고 적힌 스티커 메모를 붙이는 것과 같다고 생각하십시오. exit이 작업 은 스크립트가 끝에 도달하거나 명령문을 실행하거나(이 스크립트에서는 무한 루프이기 때문에 두 가지 모두 발생하지 않음) 인터럽트 신호(SIGINT, +에 의해 생성됨)에 의해 종료되는 경우CtrlCheartbeat

답변2

date유틸리티를 사용하여 현재 시간을 초 단위로 가져옵니다.

#!/bin/bash
lastTime=-5

while true
do
    currentTime=$(date +%s)
    elapsedTime=$((currentTime - lastTime))
    if [[ $elapsedTime -ge 5 ]]
    then
        echo 'Something different'
        lastTime=$currentTime
        i=0
    else
        # process of random duration; not important
        r=$(( 1 + RANDOM % 3 ))
        sleep ${r}s
        i=$((i + r))
        echo $i
    fi
done

편집: lastTime처음에 "다른 작업을 수행"하도록 초기 값을 변경했습니다.

답변3

이것은 내가 지금 실행하고 있는 코드입니다.페이유의 답변일부 수정 사항이 있습니다.

#!/bin/bash

t_lastdue=$(date --date='5seconds ago' +%s)

while true
do
    t_now=$(date +%s)
    t_elapsed=$((t_now - t_lastdue))
    if [ $t_elapsed -ge 5 ]
    then
        echo 'Something different'
        t_lastdue=$((t_lastdue + 5))
        i=0
    else
        # process of random duration; not important
        r=$(( 1 + RANDOM % 3 ))
        sleep "${r}s"
        i=$((i + r))
        echo "$i"
    fi
done

답변4

어쨌든 그것을 사용하고 있으므로 bash다음을 사용해야 합니다 $SECONDS.

#!/bin/bash
while [ "$SECONDS" -lt 5 ] || {
      SECONDS=$((i=0))
      echo Something different
};do  sleep "$((r=(1+RANDOM)%3))"
      echo  "$((i+=r))"
done

에서 man bash:

$SECONDS

  • 이 변수는 쉘이 시작된 이후의 시간(초)으로 확장됩니다. 이 변수에 할당하면 카운트가 할당된 값으로 재설정되고 확장된 값은 할당된 값에 할당 이후의 초 수를 더한 값이 됩니다.

아래와 같은 방법그리고다루다경향:

S=SECONDS
while   [ "$(($S<5||(i=0*($S-=5))))" -ne 0 ] ||
        echo Something different
do      sleep "$((r=(1+RANDOM)%3))"
        echo  "$((i+=r))"
done

관련 정보