sleep 명령없이 bash에서 바쁜 대기를 피하십시오.


19

나는 bash에서 다음과 같이하면 조건이 충족되기를 기다릴 수 있다는 것을 안다.

while true; do
  test_condition && break
  sleep 1
done

그러나 각 반복마다 하나의 하위 프로세스를 만듭니다 (슬립). 나는 그렇게함으로써 그들을 피할 수 있었다.

while true; do
  test_condition && break
done

그러나 그것은 많은 CPU를 사용합니다 (바쁨 대기). 하위 프로세스와 바쁜 대기를 피하기 위해 솔루션 벨로우즈를 생각해 냈지만 추악합니다.

my_tmp_dir=$(mktemp -d --tmpdir=/tmp)    # Create a unique tmp dir for the fifo.
mkfifo $my_tmp_dir/fifo                  # Create an empty fifo for sleep by read.
exec 3<> $my_tmp_dir/fifo                # Open the fifo for reading and writing.

while true; do
  test_condition && break
  read -t 1 -u 3 var                     # Same as sleep 1, but without sub-process.
done

exec 3<&-                                # Closing the fifo.
rm $my_tmp_dir/fifo; rmdir $my_tmp_dir   # Cleanup, could be done in a trap.

참고 : 일반적인 경우, 나는 read -t 1 varstdin을 소비하고 stdin이 터미널이나 파이프가 아닌 경우 작동하지 않기 때문에 fifo없이 간단히 사용할 수 없습니다 .

하위 프로세스와 더 우아한 방식으로 바쁜 대기를 피할 수 있습니까?


1
true내장이며 bash에서 하위 프로세스를 작성하지 않습니다. 바쁜 대기는 항상 나쁠 것입니다.
jordanm

@ joranm : 당신은 맞습니다 true. 질문이 업데이트되었습니다.
jfg956

fifo가없는 이유는 무엇입니까? 간단히 read -t 1 var.
ott--

@ott : 당신 말이 맞지만, 이것은 stdin을 소비합니다. 또한 stdin이 터미널이나 파이프가 아닌 경우 작동하지 않습니다.
jfg956

유지 관리가 문제라면 sleep첫 번째 예에서 와 같이 진행하는 것이 좋습니다 . 두 번째는 작동하지만 미래에 누구나 쉽게 조정할 수는 없습니다. 간단한 코드도 안전 할 가능성 이 더 큽니다 .
Kusalananda

답변:


17

최신 버전 bash(최소 v2)에서는 내장을로드 할 수 있습니다 (enable -f filename commandname 을 런타임에 . 로드 가능한 많은 내장 기능도 bash 소스와 함께 배포되며 그 sleep중 하나입니다. 물론 가용성은 OS마다 다를 수 있으며 심지어 기계마다 다를 수 있습니다. 예를 들어, openSUSE에서 이러한 내장은 package를 통해 배포됩니다 bash-loadables.

편집 : 패키지 이름을 수정하고 최소 bash 버전을 추가하십시오.


와우, 이것은 내가 찾고있는 것이고,로드 가능한 내장에 대해 확실히 배웁니다 : +1. 나는 이것을 시도 할 것이지만 가장 좋은 대답입니다.
jfg956

1
효과가있다 ! 데비안에서 패키지는 bash-builtins입니다. 소스 만 포함하고 Makefile을 편집해야하지만 sleep내장 파일 로 설치할 수있었습니다 . 감사.
jfg956

9

많은 하위 프로세스를 만드는 것은 내부 루프에서 나쁜 것입니다. sleep초당 하나의 프로세스를 작성 해도됩니다. 아무 문제 없어

while ! test_condition; do
  sleep 1
done

실제로 외부 프로세스를 피하려면 fifo를 열어 둘 필요가 없습니다.

my_tmpdir=$(mktemp -d)
trap 'rm -rf "$my_tmpdir"' 0
mkfifo "$my_tmpdir/f"

while ! test_condition; do
  read -t 1 <>"$my_tmpdir/f"
done

당신은 1 초당 땅콩 과정에 대해 옳습니다 (그러나 제 질문은 그것을 제거하는 방법을 찾는 것입니다). 짧은 버전에 대한, 그것은 하나 때문에, 나보다 좋네요 (하지만 제거 mkdir가 수행 될 때 mktemp(그렇지 않은 경우 그것이 경쟁 조건)). 또한 while ! test_condition;내 초기 솔루션보다 좋은 점 에 대해서도 마찬가지 입니다.
jfg956

7

나는 최근에 이것을 할 필요가 있었다. 외부 프로그램을 호출하지 않고 bash가 영원히 잠들 수있게하는 다음 기능을 생각해 냈습니다.

snore()
{
    local IFS
    [[ -n "${_snore_fd:-}" ]] || { exec {_snore_fd}<> <(:); } 2>/dev/null ||
    {
        # workaround for MacOS and similar systems
        local fifo
        fifo=$(mktemp -u)
        mkfifo -m 700 "$fifo"
        exec {_snore_fd}<>"$fifo"
        rm "$fifo"
    }
    read ${1:+-t "$1"} -u $_snore_fd || :
}

참고 : 이전에 파일 설명자를 열고 닫을 수있는 버전을 게시했지만 일부 시스템 에서이 작업을 수백 번 수행하면 결국 잠길 수 있습니다. 따라서 새로운 솔루션은 함수를 호출 할 때마다 파일 디스크립터를 유지합니다. Bash는 어쨌든 종료시 정리합니다.

이것은 / bin / sleep처럼 호출 될 수 있으며 요청 된 시간 동안 휴면 상태가됩니다. 매개 변수없이 호출하면 영원히 중단됩니다.

snore 0.1  # sleeps for 0.1 seconds
snore 10   # sleeps for 10 seconds
snore      # sleeps forever

내 블로그에 과도한 세부 정보가있는 글이 있습니다.


1
훌륭한 블로그 항목. 그러나 10 초 read -t 10 < <(:)read -t 10 <> <(:)기다리는 동안 즉시 돌아 오는 이유에 대한 설명을 찾아 보았지만 여전히 얻지 못했습니다.
Amir

에서 read -t 10 <> <(:)어떤 않는 <>약자?
CodeMedic

기본 프로세스 대체 <(:)가 읽기만 허용하더라도 <>는 읽기 및 쓰기를 위해 파일 디스크립터를 엽니 다. 이것은 리눅스와 리눅스가 누군가가 그것에 쓸 수 있다고 가정하게하는 해킹이므로 결코 읽지 않을 입력을 기다리면 읽기가 중단됩니다. 이 해결 방법에서 시작된다이 경우 BSD 시스템에서이 작업을 수행하지 않습니다.
볼트

3

에서 ksh93또는 mksh, sleep쉘 내장은, 대안은 대신 그 쉘을 사용하는 것이 될 수 있도록bash .

zsh또한 을 사용하여 지정된 수백 초 동안 잠을 잘 수 있는 zselect내장 (으로로드 zmodload zsh/zselect) 기능이 있습니다 zselect -t <n>.


2

사용자 yoi가 말했듯이 스크립트에서 stdin이 열리면 sleep 1 대신 다음을 사용할 수 있습니다.

read -t 1 3<&- 3<&0 <&3

Bash 버전 4.1 이상에서는 float 숫자를 사용할 수 있습니다. read -t 0.3 ...

스크립트에서 stdin 이 닫히면 ( 스크립트 가라고my_script.sh < /dev/null &) 다른 열린 디스크립터를 사용해야 합니다. 예를 들어 읽기 를 실행할 때 출력을 생성하지 않습니다 . 표준 출력 :

read -t 1 <&1 3<&- 3<&0 <&3

스크립트에서 모든 디스크립터가 닫히면 ( stdin , stdout , stderr ) (예 : 데몬이라고 함) 출력을 생성하지 않는 존재하는 파일을 찾아야합니다.

read -t 1 </dev/tty10 3<&- 3<&0 <&3

read -t 1 3<&- 3<&0 <&3와 동일합니다 read -t 0. stdin에서 시간 초과로 읽는 것입니다.
Stéphane Chazelas

1

이것은 비대화 형 쉘뿐만 아니라 로그인 쉘에서도 작동합니다.

#!/bin/sh

# to avoid starting /bin/sleep each time we call sleep, 
# make our own using the read built in function
xsleep()
{
  read -t $1 -u 1
}

# usage
xsleep 3

Mac OS X v10.12.6
b01

1
권장하지 않습니다. 여러 스크립트가 동시에 이것을 사용하는 경우 모두 stdin을 읽으려고 할 때 SIGSTOP을받습니다. 기다리는 동안 stdin이 차단됩니다. 이것을 위해 stdin을 사용하지 마십시오. 새로운 다른 파일 설명자가 필요합니다.
Normadize

1
@Normadize 무료 파일 디스크립터 사용에 대한 우려를 다루는 또 다른 답변이 있습니다 ( unix.stackexchange.com/a/407383/147685 ). 최소 버전은 read -t 10 <> <(:)입니다.
Amir

0

당신은 정말로 fifo가 필요합니까? stdin을 다른 파일 디스크립터로 리디렉션하는 것도 효과적입니다.

{
echo line | while read line; do
   read -t 1 <&3
   echo "$line"
done
} 3<&- 3<&0

고무시키는 : while 루프 안에서 bash에서 입력 읽기


1
이것은 잠을 자지 않고 있으며, 여전히 터미널에서 stdin을 소비하고 있습니다.
jfg956

0

위에서 언급 한 솔루션에 약간의 개선 (이를 기반으로 함).

bash_sleep() {
    read -rt "${1?Specify sleep interval in seconds}" -u 1 <<<"" || :;
}

# sleep for 10 seconds
bash_sleep 10

fifo의 필요성이 줄어 들었으므로 청소할 필요가 없습니다.


1
권장하지 않습니다. 여러 스크립트가 동시에 이것을 사용하는 경우 모두 stdin을 읽으려고 할 때 SIGSTOP을받습니다. 기다리는 동안 stdin이 차단됩니다. 이것을 위해 stdin을 사용하지 마십시오. 새로운 다른 파일 설명자가 필요합니다.
Normadize

@Normadize 결코 생각하지 않았다; 더 자세히 읽을 수있는 자료를 정교하게 설명해 주시겠습니까?
CodeMedic

@CodeMedic 무료 파일 디스크립터 사용에 대한 우려를 다루는 또 다른 답변 ( unix.stackexchange.com/a/407383/147685 )이 있습니다. 최소 버전은 read -t 10 <> <(:)입니다.
Amir
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.