"cat file | ./binary"와 "./binary < file"의 차이점은 무엇입니까?

"cat file | ./binary"와 "./binary < file"의 차이점은 무엇입니까?

수정할 수 없는 바이너리 파일이 있는데 다음과 같이 할 수 있습니다.

./binary < file

나는 또한 할 수 있습니다:

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

하지만

cat file | ./binary

나에게 오류가 발생합니다. 파이프로는 왜 작동하지 않는지 모르겠습니다. 3가지 경우 모두 내용은 다음과 같습니다.문서할당된 표준 입력바이너리(다른 방법을 사용하십시오):

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

내가 아는 한, 바이너리는 이 세 가지의 차이점을 알아차려서는 안 됩니다. 세 번째 경우가 작동하지 않는 이유를 누군가 설명할 수 있습니까?

그런데: 주어진 오류바이너리예:

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

하지만 내 주요 질문은 차이점이 무엇입니까?모든 프로그램이 3가지 옵션이 있습니다.

자세한 내용은 다음과 같습니다. 다시 시도했습니다.스트레스 사실 약간의 오류가 있습니다ESPIPE(불법 추구)~에서찾다 이어서EFAULT(잘못된 주소)~에서읽다오류 메시지 직전.

임시 파일을 사용하지 않고 Ruby 스크립트를 사용하여 제어하려는 바이너리는 다음과 같습니다.카라피~에서자동(UC4).

답변1

존재하다

./binary < file

binarystdin은 읽기 전용 모드로 열린 파일입니다. 파일 은 bash전혀 읽히지 않으며, 실행하는 프로세스의 파일 설명자 0(stdin)을 읽기 위해 파일을 엽니다 binary.

존재하다:

./binary << EOF
test
EOF

셸에 따라 stdin은 셸이 넣은 내용이나 파이프의 읽기 끝( , ; 및 셸이 파이프의 다른 쪽에 병렬로 쓰기)을 binary포함하는 삭제된 임시 파일(AT&T ksh, zsh, bash...)이 됩니다 . . 귀하의 경우 .test\ndashyashtest\nbash

존재하다:

cat file | ./binary

쉘에 따라 binarystdin은 파이프의 읽기 끝이거나 쓰기 방향이 꺼지고(ksh93) cat다른 쪽 끝의 내용이 기록되는 소켓 쌍의 한쪽 끝입니다.file

stdin이 일반 파일(임시 또는 비임시)인 경우 검색 가능합니다. binary시작 또는 끝으로 이동, 되감기 등이 가능합니다. 또한 이를 매핑하고 ioctl()sFIEMAP/FIBMAP과 유사한 작업을 수행할 수 있습니다( <>대신 사용하는 경우 <잘라내기/구멍 뚫기 등).

반면에 파이프와 소켓 쌍은 프로세스 간 통신의 수단이며 binary데이터 외에는 할 수 있는 일이 많지 않습니다(파이프 관련 작업과 같은 일부 작업이 있지만 일반 파일이 아닌 해당 파일에 대해 수행할 수 있습니다.)readioctl()

대부분의 경우 seek파이프를 사용할 때 응용 프로그램이 실패/불만을 일으키는 것은 이 기능이 부족하기 때문입니다. 그러나 일반 파일에서는 작동하지만 다른 유형의 파일에서는 작동하지 않는 다른 시스템 호출(예 mmap(): , ftruncate(), , fallocate())일 수 있습니다. Linux에서는 /dev/stdinfd 0이 파이프나 일반 파일에서 열릴 때 동작에도 큰 차이가 있습니다.

처리할 수 있는 명령이 많이 있습니다.탐색 가능한파일이지만 이 경우 일반적으로 표준 입력에서 열린 파일에는 적용되지 않습니다.

$ 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파일 끝에 저장된 색인을 읽은 다음 파일 내에서 아카이브 구성원을 읽어야 합니다. 그러나 여기서는 파일(첫 번째 경우 일반 파일, 두 번째 경우 파이프)이 경로 인수로 제공되고 unzip호출자 unzip가 지정한 fd를 상속하는 대신 자체적으로(보통 0이 아닌 fd에서) 열립니다. 열었습니다. 표준 입력에서 zip 파일을 읽지 않습니다. stdin은 주로 사용자 상호 작용에 사용됩니다.

binary리디렉션 없이 터미널 에뮬레이터에서 실행되는 대화형 셸의 프롬프트에서 프로그램을 실행 하면 binarystdin은 호출자 셸에서 상속되고 셸 자체는 호출자 터미널 에뮬레이터에서 상속되며 열린 pty 장치가 됩니다. 읽기+쓰기 모드(그와 비슷한 것 /dev/pts/n).

이러한 장치도 검색할 수 없습니다. 따라서 binary터미널에서 입력을 받는 것이 제대로 작동한다면 문제는 아마도 찾기와 관련이 없을 것입니다.

14가 errno(실패한 시스템 호출로 설정된 오류 코드)인 경우 대부분의 시스템에서 이는 EFAULT(잘못된 주소). read()쓰기 불가능한 메모리 주소에서 읽기가 필요한 경우 시스템 호출은 이 오류로 인해 실패합니다. 이는 fd가 뾰족한 파이프에서 읽는지 일반 파일에서 읽는지 여부와는 아무런 관련이 없으며 일반적으로 Error 1 을 나타냅니다 .

binary표준 입력( 을 사용하여)에서 열린 파일 유형을 확인할 수 fstat()있으며, 일반 파일도 아니고 tty 장치도 아닌 경우 오류가 발생합니다.

앱에 대해 더 많이 알지 못하면 말하기가 어렵습니다. 이것을 (또는 시스템에서 이에 상응하는 것) 실행하면 여기서 실패할 경우 시스템 호출이 무엇인지 이해하는 데 strace도움이 됩니다 .trusstusc


상상한 시나리오 1개매튜 아이브귀하의 질문에 대한 의견은 여기서 합리적으로 들립니다. 그의 말을 인용하자면:

데이터를 읽기 위한 버퍼 크기를 얻기 위해 파일 끝을 보고 조회가 작동하지 않는다는 사실을 잘못 처리하고 음수 크기를 할당하려고 시도하는 것 같습니다(잘못된 malloc을 처리하지 않음). 주어진 버퍼의 어떤 오류가 유효하지 않은지 읽기 위해 버퍼를 전달합니다.

답변2

다음은 설명하는 간단한 예제 프로그램입니다.Stefan Chazeras의 답변사용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

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

답변3

파이프와 리디렉션은 서로 다른 동물이라고만 말하면 충분합니다. 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()쉽게 파일을 탐색하여 파일의 모든 바이트로 이동할 수 있습니다.

파이프는 64KiB 버퍼(적어도 Linux에서는)이며 4096바이트 이하의 쓰기는 원자성이 보장되므로 검색할 수 없습니다. 즉, 자유롭게 탐색할 수 없으며 순차 읽기만 가능합니다. Python으로 명령을 구현 했습니다 tail. 리디렉션하면 마이크로초 안에 2,900만 줄의 텍스트를 찾을 수 있지만, cat파이프라인을 통해 편집하면 할 수 있는 일이 많지 않으므로 모든 것을 순서대로 읽어야 합니다.

또 다른 가능성은 바이너리가 파일을 구체적으로 열고 파이프로부터 입력을 받지 않기를 원할 수 있다는 것입니다. 일반적으로 fstat()시스템 호출을 통해 수행되며 입력이 S_ISFIFO특정 유형의 파일(파이프/명명된 파이프를 의미)에서 오는지 확인합니다.

귀하의 특정 바이너리는 그것이 무엇인지 모르기 때문에 파이프를 찾으려고 시도하지만 찾을 수 없습니다. 오류 코드 14의 정확한 의미를 이해하려면 해당 설명서를 참조하는 것이 좋습니다.

노트/bin/sh: dash(Debian Almquist Shell, Ubuntu의 기본값) 와 같은 일부 셸은 here-doc리디렉션을 구현합니다.내부 배관이므로 검색이 불가능할 수 있습니다. 요점은 동일합니다. 파이프라인은 연속적이고 쉽게 탐색할 수 없으며 탐색하려고 하면 오류가 발생합니다.

답변4

가장 큰 차이점은 오류 처리입니다.

다음 상황에서는 오류가 보고됩니다.

$ /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경우 마지막 파이프라인 구성 요소 제외 ). 따라서 변수 변경 사항은 상위 셸에 영향을 주지 않습니다.lastpipebash

$ 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

관련 정보