“cat file | ./binary”및“./binary <file”?


102

바이너리 (수정 할 수 없음)가 있으며 다음을 수행 할 수 있습니다.

./binary < file

나는 또한 할 수 있습니다 :

./binary << EOF
> "line 1 of file"
> "line 2 of file"
...
> "last line of file"
> EOF

그러나

cat file | ./binary

나에게 오류를 준다. 왜 파이프와 함께 작동하지 않는지 모르겠습니다. 세 가지 경우 모두 파일 의 내용 은 바이너리 의 표준 입력 에 다른 방식으로 제공됩니다.

  1. bash는 파일을 읽고 바이너리의 stdin에 제공합니다
  2. bash는 stdin에서 행을 읽고 (EOF까지) 이진 stdin에 제공합니다.
  3. cat은 파일 라인을 읽고 stdout에 넣고 bash는 바이너리 를 stdin으로 리디렉션합니다.

바이너리는 내가 이해하는 한 3 사이의 차이점을 인식하지 않아야합니다. 누군가 세 번째 사례가 효과가없는 이유를 설명 할 수 있습니까?

BTW : 바이너리에 의해 주어진 에러 는 :

20170116 / 125624.689-U3000011 스크립트 파일 '', 오류 코드 '14'을 (를) 읽을 수 없습니다.

그러나 내 주요 질문은 3 가지 옵션이 있는 프로그램 의 차이점은 무엇 입니까?

여기에 몇 가지 자세한 내용은 다음과 같습니다 나는 다시 그것을 시도 strace를 일부 오류 사실이 있었다 ESPIPE (불법 추구) 에서 lseek의 다음 EFAULT (잘못된 주소) 에서 읽은 바로 오류 메시지 전에.

루비 스크립트 (임시 파일을 사용하지 않고)로 제어하려고 시도한 바이너리는 Automic from UC4 (UC4)callapi의 일부입니다 .


25
쿨,가 UUOC의 바이너리에 포함 검출기. 나는 그것을 원한다.
xhienne

4
OS는 무엇입니까 (따라서 14가 errno 인 경우 어떤 것인지 알 수 있습니까)?
Stéphane Chazelas

6
프로그램이 이런 식으로 반응하는 것이 가능 하더라도 , 그것은 끔찍한 버그가 될 것입니다. stdin이 tty 일 때 stdin의 입력을 기대하는 모든 비 미친 프로그램은 stdin이 tty 일 때 작동해야하며 tty와 파일 모두에서 작동 할 수 있다면 파이프도 지원하지 않는 이유가 거의 없습니다. 아마도 프로그램의 저자는 일시적인 출혈을 겪었을 수도 있지만, isatty()거짓을 반환 하는 것은 찾아 볼 수 있거나 mmappable 파일 일 것입니다.
Henning Makholm

9
오류 코드 14는 EFAULT를 나타냅니다. 선언 한 버퍼가 유효하지 않은 경우에 발생합니다. 나는 프로그램을 밟을 것이지만 데이터를 읽기 위해 버퍼 크기를 얻기 위해 파일 끝을 찾고, 탐색이 작동하지 않는 사실을 잘못 처리하고 음수 크기를 할당하려고 시도합니다 (나쁜 malloc을 처리하지 않음) . 버퍼를 전달하여 버퍼에 지정된 오류가 유효하지 않은지 읽습니다.
Matthew Ife

3
@ xhienne 아니오, cat예방책이 있습니다. 의도 한 사용법과 같이 두 파일을 결합하는 데 사용할 수없는 것으로 보입니다.
jpmc26

답변:


150

에서

./binary < file

binarystdin은 파일을 읽기 전용 모드로 엽니 다. 참고 bash모든 파일을 읽을 수없는, 그냥 그것을 실행하는 프로세스의 파일 기술자 0 (표준 입력)에 읽기 위해 열립니다 binary에서.

에서:

./binary << EOF
test
EOF

쉘에 따라 binary의의 표준 입력은 삭제 된 임시 파일 포함 (AT & T KSH, zsh을, bash는 ...)이 될 것입니다 test\n쉘 또는 파이프 (의 독서 말까지이 말했듯을 dash, yash그리고 쉘은 기록 test\n병렬 파이프의 다른 쪽 끝에서). 귀하의 경우을 사용 bash하는 경우 임시 파일이됩니다.

에서:

cat file | ./binary

쉘에 따라 binarystdin은 파이프의 읽기 끝이거나 쓰기 방향이 종료 된 소켓 쌍의 한 쪽 끝 (ksh93)이며 다른 쪽 끝 cat의 내용을 쓰고 file있습니다.

stdin이 일반 파일 (임시 파일인지 아닌지) 인 경우 검색 할 수 있습니다. binary시작 또는 끝으로 이동하거나 되감기 등을 할 수 있습니다. 또한 ioctl()sFIEMAP / FIBMAP과 같은 작업을 수행 할 수 있습니다 ( <>대신에 사용 하는 경우 <구멍을 자르거나 구멍을 뚫을 수 있음).

반면에 파이프와 소켓 쌍은, 프로세스 간 통신 수단이다 많지 않다 binary옆 할 수 있습니다 read(일부 파이프 별과 같은 일부 작업 있기는하지만 데이터를 보내고 ioctl()그들에 할 아니라 일반 파일에 수들) .

대부분의 경우 seek파이프 작업을 할 때 응용 프로그램이 실패 / 불평하게하는 기능이 누락 되었지만 일반 파일에서는 유효하지만 다른 유형의 파일 (예 mmap(): ftruncate(), fallocate()) 에서는 유효하지 않은 다른 시스템 호출 일 수 있습니다 . Linux에서는 /dev/stdinfd 0이 파이프 나 일반 파일에있는 동안 열 때 동작에 큰 차이가 있습니다 .

검색 가능한 파일 만 처리 할 수있는 많은 명령이 있지만 일반적으로 stdin에서 열린 파일에는 해당되지 않습니다.

$ unzip -l file.zip
Archive:  file.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
       11  2016-12-21 14:43   file
---------                     -------
       11                     1 file
$ unzip -l <(cat file.zip)
     # more or less the same as cat file.zip | unzip -l /dev/stdin
Archive:  /proc/self/fd/11
  End-of-central-directory signature not found.  Either this file is not
  a zipfile, or it constitutes one disk of a multi-part archive.  In the
  latter case the central directory and zipfile comment will be found on
  the last disk(s) of this archive.
unzip:  cannot find zipfile directory in one of /proc/self/fd/11 or
        /proc/self/fd/11.zip, and cannot find /proc/self/fd/11.ZIP, period.

unzip파일 끝에 저장된 색인을 읽은 다음 파일 내에서 아카이브 멤버를 읽으십시오. 그러나 여기에서 파일 (첫 번째 경우 정규 파이프, 두 번째 파이프)은에 대한 경로 인수로 제공되며 부모에 의해 이미 열린 fd를 상속하는 대신 파일 자체 (일반적으로 0 이외의 fd unzip에서)를 unzip엽니 다. stdin에서 zip 파일을 읽지 않습니다. stdin은 주로 사용자 상호 작용에 사용됩니다.

binary터미널 에뮬레이터에서 실행되는 대화 형 쉘의 프롬프트에서 리디렉션하지 않고 자신 의 것을 실행하면 binarysstdin은 부모 쉘에서 상속되며, 그 자체는 부모에서 터미널 에뮬레이터에서 상속됩니다. pty 장치가 읽기 + 쓰기 모드로 열립니다 (같은 것 /dev/pts/n).

이러한 장치는 찾을 수 없습니다. 따라서 binary터미널에서 입력을받을 때 정상적으로 작동하면 문제는 추구하는 것이 아닙니다.

14가 errno (시스템 호출 실패로 설정된 오류 코드) 인 경우 대부분의 시스템에서 EFAULT( Bad address )가됩니다. read()쓰기 권한이 없습니다 메모리 주소로 읽어 요구한다면, 시스템 호출은 오류와 함께 실패합니다. 이는 fd가 포인트에서 파이프 또는 일반 파일로 데이터를 읽는지 여부와 무관하며 일반적으로 버그 1을 나타냅니다 .

binarystdin에서 열린 파일의 유형을 (으로 fstat()) 결정하고 일반 파일이나 tty 장치가 아닌 경우 버그가 발생합니다.

응용 프로그램에 대해 더 많이 알지 못하면 말하기가 어렵습니다. 아래를 실행 strace(또는 truss/ tusc시스템에 상응하는) 우리가 어떤 여기에 실패하는 경우 시스템 호출이 무엇인지 확인하는 데 도움 수 있습니다.


1 Matthew Ife 가 귀하의 질문에 대한 의견을 제시 한 시나리오 는 여기서 그럴듯하게 들립니다. 그를 인용 :

나는 데이터를 읽기위한 버퍼 크기를 얻기 위해 파일 끝을 찾고, 탐색이 작동하지 않는 사실을 잘못 처리하고 음수 크기를 할당하려고 시도합니다 (잘못된 malloc을 처리하지 않음). 버퍼를 전달하여 버퍼에 지정된 오류가 유효하지 않은지 읽습니다.


14
매우 흥미 롭습니다 ... 이것은 스타일의 방향 전환 된 표준 입력을 찾을 수 있다고 들었습니다 ./binary < file.
David Z

2
@DavidZ 그것은 opened 된 파일이며 ed 된 파일 과 동일하게 동작합니다 open. 그것은 단지 부모 프로세스로부터 상속받은 것 같지만 그렇게 드물지 않습니다.
hobbs

3
시스템에 strace 또는 이와 유사한 도구 가 포함 된 경우 바이너리가 실패한 시스템 호출을 확인하는 데 사용될 수 있습니다.
pabouk

2
"또한 잘릴 수도 있고 구멍을 뚫을 수도 있습니다." - 음 ... 아니. 파일이 읽기 전용 모드로 열려 있습니다. 이를 위해서는 프로그램이 쓰기 모드로 열어야합니다. 그러나 직접 작성하기위한 인터페이스가 없거나 열린 파일에 해당하는 "the"디렉토리 항목을 찾기위한 인터페이스가 없기 때문에 쓰기 모드에서 열 수 없습니다 (이러한 두 개의 덴트 리가있는 경우 어떻게됩니까?). . 파일을 스 태팅 한 다음 파일 시스템에서 동일한 inode 번호를 가진 오브젝트를 스캔해야합니다. 너무 느릴 것입니다.
Kevin

1
@ StéphaneChazelas : 아, open("/proc/self/fd/0", O_RDWR)삭제 된 파일에서도 작동합니다. 바보 야 : P. stdin이에서 리디렉션 된 상태에서 a.out이 실행되기 전에 연결을 echo foo>foo; (sleep 0.5; ll -L /proc/self/fd/0; strace ./a.out; ll -L /proc/self/fd/0) < foo & sleep 0.1 && rm foo해제합니다 . foofoo
Peter Cordes

46

여기서 설명하는 간단한 예시적인 프로그램의 스테판 Chazelas가 '대답을 이용한 lseek(2)입력에은 :

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(void)
{
    int c;
    off_t off;
    off = lseek(0, 10, SEEK_SET);
    if (off == -1)
    {
        perror("Error");
        return -1;
    }
    c = getchar();
    printf("%c\n", c);
}

테스트 :

$ make seek
cc     seek.c   -o seek
$ cat foo
abcdefghijklmnopqrstuwxyz
$ ./seek < foo
k
$ ./seek <<EOF
> abcdefghijklmnopqrstuvwxyz
> EOF
k
$ cat foo | ./seek
Error: Illegal seek

파이프는 구할 수 없으며 프로그램이 파이프에 대해 불평 할 수있는 곳입니다.


21

파이프와 방향 전환은 다른 동물들입니다. here-doc리디렉션 ( <<) 또는 stdin 리디렉션 을 사용 < 하면 텍스트가 얇은 공기에서 나오지 않습니다. 실제로는 파일 설명자 (또는 임시 파일 인 경우)에 들어가고 바이너리의 stdin이 가리키는 곳입니다.

다음은 bash's소스 코드 redir.c 파일 (버전 4.3) 에서 발췌 한 내용입니다 .

/* Create a temporary file holding the text of the here document pointed to
   by REDIRECTEE, and return a file descriptor open for reading to the temp
   file.  Return -1 on any error, and make sure errno is set appropriately. */
static int
here_document_to_fd (redirectee, ri)

따라서 리디렉션은 기본적으로 파일로 처리 될 수 있으므로 바이너리는 seek()파일을 쉽게 탐색하거나 파일을 통해 파일의 모든 바이트로 이동할 수 있습니다.

파이프는 4096 바이트 이하의 쓰기가 원 자성으로 보장 된 64KiB (최소한 Linux에서는)의 버퍼이므로 검색 할 수 없습니다. 즉, 자유롭게 탐색 할 수 없으며 순차적으로 만 읽을 수 있습니다. 한때 tail파이썬으로 명령을 구현 했습니다. 리디렉션하면 2,900 만 줄의 텍스트를 마이크로 초 단위로 검색 할 수 있지만 cat파이프를 통해 연결하면 수행 할 수있는 작업이 없으므로 모든 내용을 순차적으로 읽어야합니다.

또 다른 가능성은 바이너리가 파일을 구체적으로 열고 파이프에서 입력을 받고 싶지 않을 수도 있습니다. 일반적으로 fstat()시스템 호출을 통해 수행되며 입력이 S_ISFIFO파일 형식 (파이프 / 명명 된 파이프를 나타냄) 에서 오는지 확인합니다 .

특정 바이너리는 그것이 무엇인지 알지 못하기 때문에 아마도 탐색을 시도하지만 파이프를 찾을 수는 없습니다. 오류 코드 14가 정확히 무엇을 의미하는지 확인하려면 해당 설명서를 참조하는 것이 좋습니다.

참고 : 대시 (데비안 Almquist 쉘, /bin/sh우분투 기본 ) 와 같은 일부 쉘 here-doc파이프 내부 에서 리디렉션을 구현 하므로 검색하지 못할 수 있습니다. 포인트는 동일하게 유지됩니다. 파이프는 순차적이며 쉽게 탐색 할 수 없으므로 그렇게하면 오류가 발생합니다.


Stephane의 대답은 here-docs는 파이프로 구현할 수 있으며 일반적인 쉘 dash은 그렇게 할 수 있다고 말합니다 . 이 답변은 bash에서 관찰 된 동작을 설명하지만 다른 쉘에서 그 동작을 보장하지는 않습니다.
Peter Cordes

@PeterCordes는 절대적으로 그렇게되었으며 방금 dash시스템에서 확인했습니다 . 나는 이전에 그것을 몰랐다. 지적 해 주셔서 감사합니다
Sergiy Kolodyazhnyy

또 다른 의견 : fstat()stdin에서 파이프인지 확인하는 데 사용합니다. stat경로 이름을 사용합니다. 그러나 실제로 시도하는 lseek것은 fd가 이미 열린 후에 찾을 수 있는지 확인하는 가장 현명한 방법 일 것입니다.
Peter Cordes

5

가장 큰 차이점은 오류 처리에 있습니다.

다음과 같은 경우 오류가보고됩니다

$ /bin/cat < z.txt
-bash: z.txt: No such file or directory
$ echo $?
1

다음과 같은 경우 오류가보고되지 않습니다.

$ cat z.txt | /bin/cat
cat: z.txt: No such file or directory
$ echo $?
0

bash를 사용하면 PIPESTATUS를 계속 사용할 수 있습니다.

$ cat z.txt | /bin/cat
cat: z.txt: No such file or directory
$ echo ${PIPESTATUS[0]}
1

그러나 명령을 실행 한 직후에만 사용할 수 있습니다.

$ cat z.txt | /bin/cat
cat: z.txt: No such file or directory
$ echo $?
0
$ echo ${PIPESTATUS[0]}
0
# oops !

바이너리 대신 쉘 함수를 사용할 때 또 다른 차이점이 있습니다. 에서 bash파이프 라인의 일부인 함수는 하위 셸에서 실행되므로 ( lastpipe옵션이 활성화되어 bash있고 비대화 형인 경우 마지막 파이프 라인 구성 요소는 제외 ) 변수 변경은 상위 셸에 영향을 미치지 않습니다.

$ a=a
$ b=b
$ x(){ a=x;}
$ y(){ b=y;}

$ echo $a $b
a b

$ x | y
$ echo $a $b
a b

$ cat t.txt | y
$ echo $a $b
a b

$ x | cat
$ echo $a $b
a b

$ x < t.txt
$ y < t.txt
$ echo $a $b
x y

4
따라서 오류 처리 >는 쉘에서 수행되지만 파이프에서는 텍스트를 생성하는 명령으로 수행됩니다. 승인. 그러나이 특정 질문에서 OP는 기존 파일을 사용하므로 문제가 아니며 바이너리에서 오류가 발생합니다.
Sergiy Kolodyazhnyy 2012 년

1
그것은 대부분 요점 옆에 있지만이 대답은 일반적인 경우 에이 Q & A와 관련이 있으며 대부분 정확하므로 그러한 공감대가 가치가 있다고 생각하지 않습니다.
Stéphane Chazelas

@Serg : 쉘을 명령 행으로 사용할 때는 중요하지 않습니다. 그러나 스크립트에서 오류 처리는 매우 중요 할 수 있습니다.
Vouze
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.