파이프가 비어 있는지 확인하고 데이터가 없으면 명령을 실행하는 방법은 무엇입니까?


42

bash 스크립트로 줄을 파이프로 묶어 파이프에 데이터가 있는지 확인하고 프로그램에 공급하기 전에 파이프에 데이터가 있는지 확인하고 싶습니다.

내가 찾은 검색 test -t 0하지만 여기서 작동하지 않습니다. 항상 false를 반환합니다. 파이프에 데이터가 있는지 확인하는 방법은 무엇입니까?

예:

echo "string" | [ -t 0 ] && echo "empty" || echo "fill"

산출: fill

echo "string" | tail -n+2 | [ -t 0 ] && echo "empty" || echo "fill"

산출: fill

앞서 언급 한 파이프 라인이 출력을 생성하는지 테스트하는 표준 / 정규 방법 과 달리 ? 입력을 프로그램으로 전달하려면 입력을 보존해야합니다. 이것은 한 프로세스에서 다른 프로세스로 출력을 파이프하는 방법을 일반화 하지만 첫 번째 출력이있는 경우에만 실행합니까? 이메일 발송에 중점을 둡니다.


답변:


37

일반적으로 사용 가능한 쉘 유틸리티를 사용하여 파이프의 내용을 들여다 볼 수 없으며 파이프의 문자를 읽은 다음 다시 넣을 수있는 방법도 없습니다. 파이프에 데이터가 있음을 알 수있는 유일한 방법은 바이트를 읽는 것입니다. 그런 다음 해당 바이트를 대상으로 가져와야합니다.

그렇게하십시오 : 1 바이트를 읽으십시오; 파일의 끝을 감지하면 입력이 비어있을 때 수행 할 작업을 수행하십시오. 바이트를 읽는 경우 입력이 비어 있지 않은 경우 수행하려는 작업을 포크하고 해당 바이트를 파이프로 보내고 나머지 데이터를 파이프하십시오.

first_byte=$(dd bs=1 count=1 2>/dev/null | od -t o1 -A n | tr -dc 0-9)
if [ -z "$first_byte" ]; then
  # stuff to do if the input is empty
else
  {
    printf "\\$first_byte"
    cat
  } | {
    # stuff to do if the input is not empty
  }      
fi

ifne에서 유틸리티 조이 헤스의 moreutils는 입력이 비어 있지 않은 경우 명령을 실행합니다. 일반적으로 기본적으로 설치되지 않지만 대부분의 유닉스 변형에서 사용 가능하거나 쉽게 빌드 할 수 있어야합니다. 입력이 비어 있으면 ifne아무것도하지 않고 상태 0을 리턴하는데, 이는 성공적으로 실행중인 명령과 구별 할 수 없습니다. 입력이 비어있는 경우 무언가를 수행하려는 경우, 성공 사례가 구별 가능한 오류 상태를 리턴하도록하여 수행 할 수있는 명령이 0을 리턴하지 않도록 배열해야합니다.

ifne sh -c 'do_stuff_with_input && exit 255'
case $? in
  0) echo empty;;
  255) echo success;;
  *) echo failure;;
esac

test -t 0이것과 아무 관련이 없습니다. 표준 입력이 터미널인지 테스트합니다. 어떤 입력을 사용할 수 있는지 여부에 대해서는 어떤 식 으로든 말하지 않습니다.


STREAMS 기반 파이프 (Solaris HP / UX)가있는 시스템에서 I_PEEK ioctl을 사용하여 파이프를 소비하지 않고 파이프의 내용을 엿볼 수 있다고 생각합니다.
Stéphane Chazelas

@ StéphaneChazelas 불행히도 * BSD의 파이프 / fifo에서 데이터를 들여다 볼 수있는 방법이 없으므로 파이프 의 실제 데이터를 얼마나 많이 가지고 있는지가 아니라 파이프에서 실제 데이터를 반환 할 수 있는 휴대용 peek 유틸리티 를 구현할 관점이 없습니다. (4.4 BSD, 386BSD 등에서 파이프는 소켓 쌍 으로 구현 되었지만 * BSD의 이후 버전에서는 양방향으로 유지되었지만 생략되었습니다).
mosvy

bash에는 a 를 통해 노출 된 입력을 확인 하는 루틴이 있습니다read -t 0 (이 경우 t는 시간 초과를 의미합니다).
이삭

11

간단한 해결책은 ifne명령 을 사용하는 것입니다 (입력이 비어 있지 않은 경우). 일부 배포판에서는 기본적으로 설치되지 않습니다. moreutils대부분의 배포판 에서 패키지의 일부입니다 .

ifne 표준 입력이 비어 있지 않은 경우에만 지정된 명령을 실행합니다.

표준 입력이 비어 있지 않으면 ifne주어진 명령으로 전달됩니다.


2
2017 년 현재 Mac 또는 Ubuntu에는 기본적으로 제공되지 않습니다.
Sridhar Sarnobat '

7

오래된 질문이지만 내가 한 것처럼 누군가가 그것을 만난 경우 : 내 해결책은 시간 초과로 읽는 것입니다.

while read -t 5 line; do
    echo "$line"
done

stdin비어 있으면 5 초 후에 반환됩니다. 그렇지 않으면 모든 입력을 읽고 필요에 따라 처리 할 수 ​​있습니다.


나는 아이디어를 좋아하지만 -t슬프게도 POSIX의 일부가 아닙니다 : pubs.opengroup.org/onlinepubs/9699919799/utilities/read.html
JepZ

6

stdin (0)의 파일 디스크립터가 열려 있는지 또는 닫혀 있는지 확인하십시오.

[ ! -t 0 ] && echo "stdin has data" || echo "stdin is empty"

일부 데이터를 전달하고 일부 데이터가 있는지 확인하려는 경우 FD를 전달하므로 좋은 테스트가 아닙니다.
Jakuje

1
[ -t 0 ]fd 0 이 닫혔거나 열려 있지 않은지 tty에 열려 있는지 확인합니다 .
mosvy

@mosvy 스크립트에서 해당 솔루션을 사용하는 데 어떻게 영향을 미치는지 자세히 설명해 주시겠습니까? 작동하지 않는 경우가 있습니까?
JepZ

@JepZ 응? ./that_script </dev/null=> "stdin에 데이터가 있습니다". 또는 ./that_script <&-stdin이 실제로 닫히도록 합니다.
mosvy

5

test -s /dev/stdin명시 적 서브 쉘에서 사용할 수도 있습니다 .

# test if a pipe is empty or not
echo "string" | 
    (test -s /dev/stdin && echo 'pipe has data' && cat || echo 'pipe is empty')

echo "string" | tail -n+2 | 
    (test -s /dev/stdin && echo 'pipe has data' && cat || echo 'pipe is empty')

: | (test -s /dev/stdin && echo 'pipe has data' && cat || echo 'pipe is empty')

8
나를 위해 작동하지 않습니다. 항상 파이프가 비어 있다고 말합니다.
amphetamachine

2
Mac에서는 작동하지만 Linux에서는 작동하지 않습니다.
피크

3

bash에서 :

read -t 0 

입력에 데이터가 있는지 감지합니다 (아무것도 읽지 않음). 그런 다음 입력을 읽을 수 있습니다 (읽기 실행시 입력을 사용할 수있는 경우).

if     read -t 0
then   read -r input
       echo "got input: $input"
else   echo "No data to read"
fi

참고 : 타이밍에 따라 다릅니다. 입력 한 시간에 입력에 이미 데이터가 있는지 여부를 감지합니다 read -t.

예를 들어

{ sleep 0.1; echo "abc"; } | read -t 0; echo "$?"

출력은 1(읽기 실패, 즉 빈 입력)입니다. echo는 일부 데이터를 기록하지만 첫 바이트를 시작하고 쓰는 속도가 빠르지 read -t 0않으므로 프로그램이 아직 아무것도 작성하지 않았기 때문에 입력이 비어 있다고보고합니다.


github.com/bminor/bash/blob/…- 다음은 bash가 파일 디스크립터에있는 것을 감지하는 방법의 소스입니다.
Pavel Patrin

감사합니다 @PavelPatrin
Isaac

1
@PavelPatrin 작동하지 않습니다 . 귀하의 링크에서 분명히 알 수 있듯이 작동하기 bash위해 a select()또는 a 또는 둘 다 수행 ioctl(FIONREAD)하지는 않습니다. read -t0고장났다. 사용하지 마십시오
mosvy

우오, 오늘 나는 두 시간 동안 무엇이 잘못되었는지 이해하려고 노력합니다! @mosvy 감사합니다!
Pavel Patrin

3

유닉스에서 읽을 수있는 데이터가 있는지 확인하는 쉬운 방법 중 하나는 FIONREADioctl을 사용하는 것입니다.

나는 그것을하는 표준 유틸리티를 생각할 수 없으므로 여기에 그것을하는 사소한 프로그램이 있습니다 ( ifnemoreutils IMHO ;-) 보다 낫습니다 ).

fionread [ prog args ... ]

stdin에 사용 가능한 데이터가 없으면 상태 1로 종료됩니다 prog. 데이터가 있으면 실행 됩니다. prog지정 하지 않으면 상태 0으로 종료됩니다.

즉시 사용 가능한 poll데이터에만 관심이있는 경우 통화 를 제거 할 수 있습니다. 파이프뿐만 아니라 대부분의 fd에서 작동합니다.

fionread.c

#include <unistd.h>
#include <poll.h>
#include <sys/ioctl.h>
#ifdef __sun
#include <sys/filio.h>
#endif
#include <err.h>

int main(int ac, char **av){
        int r; struct pollfd pd = { 0, POLLIN };
        if(poll(&pd, 1, -1) < 0) err(1, "poll");
        if(ioctl(0, FIONREAD, &r)) err(1, "ioctl(FIONREAD)");
        if(!r) return 1;
        if(++av, --ac < 1) return 0;
        execvp(*av, av);
        err(1, "execvp %s", *av);
}

이 프로그램은 실제로 작동합니까? POLLHUP빈 케이스를 처리하기 위해 이벤트 를 기다릴 필요가 없습니까? 파이프의 다른 쪽 끝에 여러 파일 설명이있는 경우에도 작동합니까?
질 'SO-정지 존재 악마'

그렇습니다. POLLHUP은 폴에 의해서만 리턴되므로 POLLIN을 사용하여 POLLHUP을 기다려야합니다. 파이프 끝에 몇 개의 열린 핸들이 있는지는 중요하지 않습니다.
mosvy

perl에서 FIONREAD를 실행하는 방법에 대해서는 unix.stackexchange.com/search?q=FIONREAD+user%3A22565 를 참조하십시오 (컴파일러보다 일반적으로 사용 가능)
Stéphane Chazelas

FIONREAD (또는 HAVE_SELECT)를 사용하는 루틴 은 bash here에서 구현됩니다 .
이삭

2

짧고 비밀스러운 원 라이너를 좋아하는 경우 :

$ echo "string" | grep . && echo "fill" || echo "empty"
string
fill
$ echo "string" | tail -n+2 | grep . && echo "fill" || echo "empty"
empty

원래 질문의 예를 사용했습니다. -qgrep과 함께 파이프 데이터 사용 옵션을 원하지 않는 경우


0

첫 줄 전체를 읽는 것이 좋다면 이것은 bash에서 합리적인 ifne 구현 인 것처럼 보입니다.

ifne () {
        read line || return 1
        (echo "$line"; cat) | eval "$@"
}


echo hi | ifne xargs echo hi =
cat /dev/null | ifne xargs echo should not echo

5
read또한 반환합니다 잘못된 입력이 비 비어 있지만 개행 문자를 포함하지, 경우 read입력에 대한 몇 가지 처리를 수행하고 당신이 그것을 호출하지 않는 한 줄 이상을 읽을 수 있습니다 IFS= read -r line. echo임의의 데이터에는 사용할 수 없습니다.
Stéphane Chazelas

0

이것은 나를 사용하여 작동합니다. read -rt 0

데이터가없는 원래 질문의 예 :

echo "string" | tail -n+2 | if read -rt 0 ; then echo has data ; else echo no data ; fi

아니요, 작동하지 않습니다. { sleep .1; echo yes; } | { read -rt0 || echo NO; cat; }(false negative) 및 true | { sleep .1; read -rt0 && echo YES; }(false positive)로 시도하십시오 . 실제로, bash read쓰기 전용 모드로 열린 fds에 의해 속일 수 있습니다 { read -rt0 && echo YES; cat; } 0>/tmp/foo. 그것이하는 것처럼 보이는 것은 select(2)그 fd입니다.
mosvy

... 및 select반환 여부 read(2)에 관계없이 fd가 "ready"로 fd를 반환 EOF합니다. 결론 : read -t0에서 졌습니다 bash. 사용하지 마십시오.
mosvy

@mosvy bashbug로보고 했습니까?
이삭

@mosvy { sleep .1; echo yes; } | { read -rt0 || echo NO; cat; }(읽기 실행시) 입력이 없기 때문에 false 음수가 아닙니다. 나중에 입력 수 있습니다 (고양이).
이삭

@mosvy r 옵션이 탐지에 영향을주는 이유는 무엇입니까 ? : echo "" | { read -t0 && echo YES; }YES를 인쇄하지만 echo "" | { read -rt0 && echo YES; }그렇지 않습니다.
이삭
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.