"리디렉션"과 "파이프"의 차이점은 무엇입니까?


204

이 질문은 약간 어리석은 것처럼 들릴 수 있지만 리디렉션과 파이프의 차이점을 실제로 볼 수는 없습니다.

리디렉션은 stdout / stdin / stderr를 리디렉션하는 데 사용됩니다 (예 :) ls > log.txt.

파이프는 명령의 출력을 다른 명령에 대한 입력으로 제공하는 데 사용됩니다 (예 :) ls | grep file.txt.

그러나 왜 같은 일에 대해 두 명의 운영자가 있습니까?

ls > grep출력을 전달하기 위해 쓰지 않는 이유 는 무엇입니까? 내가 무엇을 놓치고 있습니까?

답변:


223

파이프는 다른 프로그램이나 유틸리티로 출력을 전달하는 데 사용됩니다 .

리디렉션은 출력을 파일이나 스트림 으로 전달하는 데 사용됩니다 .

예 : thing1 > thing2vsthing1 | thing2

thing1 > thing2

  1. 쉘은 이름이 지정된 프로그램을 실행합니다 thing1
  2. thing1출력되는 모든 파일은이라는 파일에 배치됩니다 thing2. (참고- thing2존재하는 경우 덮어 씁니다)

프로그램의 출력을 thing1이라는 프로그램으로 전달 thing2하려면 다음을 수행하십시오.

thing1 > temp_file && thing2 < temp_file

어느 것

  1. 프로그램을 실행 thing1
  2. 출력을 파일 이름으로 저장 temp_file
  3. thing2키보드에있는 사람 temp_file이 입력 내용을 입력 한 것처럼 가장하는 이라는 프로그램을 실행 하십시오.

그러나 그것은 어수선하기 때문에 파이프를 더 간단한 방법으로 만들었습니다. thing1 | thing2와 같은 일을thing1 > temp_file && thing2 < temp_file

의견의 질문에 대한 자세한 내용을 제공하도록 편집하십시오.

경우 >수 모두 "프로그램에 전달"와 "파일에 쓸 '을 시도, 그것은 두 방향에서 문제가 발생할 수 있습니다.

첫 번째 예 : 파일에 쓰려고합니다. 덮어 쓰려는 해당 이름의 파일이 이미 있습니다. 그러나 파일은 실행 가능합니다. 아마도이 파일을 실행하여 입력을 전달하려고 시도했을 것입니다. 출력을 새 파일 이름으로 쓴 다음 파일 이름을 바꾸는 것과 같은 작업을 수행해야합니다.

두 번째 예 : Florian Diesch가 지적했듯이 시스템의 다른 이름과 동일한 이름 (실행 경로)에 다른 명령이 있으면 어떻게됩니까? 현재 폴더에 해당 이름의 파일을 만들려고한다면 막힐 것입니다.

셋째 , 명령을 잘못 입력하면 명령이 존재하지 않는다는 경고가 표시되지 않습니다. 입력 ls | gerp log.txt하면 바로 알려줍니다 bash: gerp: command not found. >둘 다 의미하는 경우 단순히 새 파일을 만들 것입니다 (그런 다음 무엇을 해야할지 모르겠다는 경고 log.txt).


감사합니다. thing1 > temp_file && thing2 < temp_file파이프로 더 쉽게 할 것을 언급 했습니다. 그러나 왜 안 다시 사용 >예 :이 작업을 수행하는 연산자 thing1 > thing2명령에 thing1thing2? 왜 여분의 연산자 |입니까?
John Threepwood

1
"출력을 가져 와서 파일에 기록"은 "출력을 만들어 다른 프로그램으로 전달"과 다른 조치입니다. 내 답변에 대한 더 많은 생각을 편집하겠습니다 ...
David Oneill

1
@JohnThreepwood 그들은 다른 의미를 가지고 있습니다. less예를 들어, 이라는 파일로 무언가를 리디렉션하려면 어떻게해야 합니까? thing | less그리고 thing > less그들은 다른 일을 같이 완벽하게 다릅니다. 당신이 제안하는 것은 모호성을 만듭니다.
Darkhogg

"thing1> temp_file"은 "thing1 | tee temp_file"의 구문 설탕 일 뿐이라고 말하는 것이 정확합니까? 티에 대해 알아 낸 이후로 리디렉션을 거의 사용하지 않습니다.
Sridhar Sarnobat

2
@ Sridhar-Sarnobat 아니오, tee명령이 다르게 작동합니다. tee화면 ( stdout) 파일 모두에 출력을 씁니다 . 리디렉션은 파일 수행합니다.
David Oneill

22

리디렉션을 사용하는 것이 훨씬 더 어렵고 오류가 발생하기 쉬운 foo > bar명명 된 명령이 있는지 여부에 따라 의미 가 결정 bar됩니다. 파일로 리디렉션 할 때마다 먼저 대상 파일과 같은 이름의 명령이 있는지 확인해야했습니다.


env 변수 bar의 일부인 디렉토리에서 쓰는 경우에만 문제가됩니다 $PATH. / bin과 같은 경우에는 문제가 될 수 있습니다. 그러나 그렇더라도 bar쉘은 실행 파일을 찾을 bar뿐만 아니라 실제로 실행할 수 있도록 실행 권한 세트를 가져야 합니다. 기존 파일을 덮어 쓰는 것이 우려되는 경우 noclober셸 옵션을 사용하면 기존 파일을 리디렉션으로 덮어 쓰지 않아야합니다.
Sergiy Kolodyazhnyy

13

유닉스 및 리눅스 시스템 관리 핸드북에서 :

리디렉션

셸은 <,> 및 >> 기호를 명령의 입력 또는 출력을 파일 로 또는 파일 에서 다시 라우팅하기위한 지침으로 해석 합니다 .

파이프

하나 개의 STDOUT을 연결하려면 명령 의 STDIN에 또 다른 을 사용하여 | 일반적으로 파이프라고하는 기호.

내 해석은 다음과 같습니다. 명령하는 명령 인 경우 파이프를 사용하십시오. 파일로 또는 파일에서 출력하는 경우 리디렉션을 사용하십시오.


12

두 연산자 사이에는 중요한 차이점이 있습니다.

  1. ls > log.txt ->이 명령은 출력을 log.txt 파일로 보냅니다.

  2. ls | grep file.txt->이 명령은 파이프 ( |)를 사용하여 ls의 출력을 grep 명령으로 전송하고 grep 명령은 이전 명령이 제공 한 입력에서 file.txt를 검색합니다.

첫 번째 시나리오를 사용하여 동일한 작업을 수행해야한다면 다음과 같습니다.

ls > log.txt; grep 'file.txt' log.txt

따라서 파이프 (with |)는 출력을 다른 명령으로 보내는 데 사용되는 반면 리디렉션 (with >)은 출력을 일부 파일로 리디렉션하는 데 사용됩니다.


3

둘 사이에는 큰 구문상의 차이가 있습니다.

  1. 리디렉션은 프로그램에 대한 인수입니다
  2. 파이프는 두 명령을 분리합니다

다음과 같은 리디렉션을 생각할 수 있습니다 cat [<infile] [>outfile]. 이것은 순서가 중요하지 않음을 의미합니다 : cat <infile >outfile와 동일합니다 cat >outfile <infile. 당신은 다른 인수와 함께 리디렉션 혼합 수 cat >outfile <infile -bcat <infile -b >outfile모두 완벽하게 정상적으로입니다. 또한 하나 이상의 입력 또는 출력을 함께 묶을 수 있습니다 (입력은 순차적으로 읽히고 모든 출력은 각 출력 파일에 기록됩니다) cat >outfile1 >outfile2 <infile1 <infile2. 리디렉션의 대상 또는 소스는 파일 이름 또는 스트림 이름 (최소한 bash에서 & 1과 같은) 일 수 있습니다.

그러나 파이프는 하나의 명령을 다른 명령과 완전히 분리하므로 인수와 함께 사용할 수 없습니다.

[command1] | [command2]

파이프는 command1의 표준 출력에 기록 된 모든 내용을 command2의 표준 입력으로 보냅니다.

배관과 방향 전환을 결합 할 수도 있습니다. 예를 들면 다음과 같습니다.

cat <infile >outfile | cat <infile2 >outfile2

첫 번째 cat는 infile에서 행을 읽은 다음 각 행을 outfile에 쓰고 두 번째 행으로 보냅니다 cat.

두 번째 cat에서 표준 입력은 먼저 파이프 (infile의 내용)에서 읽은 다음 infile2에서 읽은 다음 각 줄을 outfile2에 씁니다. 이를 실행 한 후 outfile은 infile의 사본이되고 outfile2에는 infile과 infile2가 포함됩니다.

마지막으로, "여기서 문자열"리디렉션 (bash 패밀리 만 해당)과 백틱을 사용하여 실제로 예제와 비슷한 작업을 수행합니다.

grep blah <<<`ls`

같은 결과를 줄 것이다

ls | grep blah

그러나 리디렉션 버전은 먼저 ls의 모든 출력을 버퍼 (메모리)로 읽은 다음 한 번에 한 줄씩 grep하기 위해 해당 버퍼를 공급하는 반면 파이프 버전은 ls에서 각 줄을 가져옵니다. 그 줄을 grep에 전달하십시오.


1
Nitpick : 한 fd를 다른 fd로 리디렉션하면 순서가 리디렉션됩니다. echo yes 1>&2 2>/tmp/blah; wc -l /tmp/blah; echo yes 2>/tmp/blah 1>&2; wc -l /tmp/blah또한 파일로의 리디렉션은 마지막 리디렉션 만 사용합니다. echo yes >/tmp/blah >/tmp/blah2에 쓸 것 /tmp/blah2입니다.
muru

2
리디렉션은 실제로 프로그램에 대한 주장이 아닙니다. 프로그램은 출력이가는 곳 (또는 입력이 오는 곳)을 알거나 신경 쓰지 않습니다. 프로그램을 실행하기 전에 bash에게 물건을 배열하는 방법을 알려주는 방법입니다.
Alois Mahdal

3

참고 :이 답변은 최신 메커니즘에 대한 본인의 이해를 반영하며,이 사이트와 unix.stackexchange.com 의 동료가 답변을 연구하고 읽은 결과를 반영 하며 시간이 지남에 따라 업데이트됩니다. 주저하지 말고 의견을 말하거나 개선을 제안하십시오. 또한 syscall이 strace명령을 사용 하여 쉘에서 어떻게 작동하는지 보려고 제안합니다 . 또한 내부 또는 시스템 콜의 개념에 겁 먹지 마십시오. 쉘이 어떻게 작동하는지 이해하기 위해 그것들을 알거나 사용할 필요는 없지만 분명히 이해하는 데 도움이됩니다.

TL; DR

  • |파이프는 디스크의 항목과 연관되어 있지 않으므로 inode 수의 디스크 파일 시스템이 없지만 ( 커널 공간의 pipefs 가상 파일 시스템에 inode가 있음 ) 경로 재 지정에는 종종 디스크 항목이 있고 이에 해당하는 파일이 포함됩니다. 아이 노드.
  • 파이프를 사용할 lseek()수 없으므로 명령이 일부 데이터를 읽은 다음 되감기를 할 수 없지만 리디렉션 >하거나 <일반적으로 파일 lseek()일 수 있는 파일 인 경우 명령은 원하는대로 탐색 할 수 있습니다.
  • 경로 재 지정은 파일 디스크립터에 대한 조작이며 여러 가지가 있습니다. 파이프에는 두 개의 파일 디스크립터 만 있습니다. 하나는 왼쪽 명령 용이고 다른 하나는 오른쪽 명령 용입니다.
  • 표준 스트림 및 파이프의 리디렉션은 모두 버퍼링됩니다.
  • 파이프는 거의 항상 포크를 포함하므로 프로세스 쌍이 관련됩니다. 리디렉션 – 항상 그런 것은 아니지만 결과 파일 디스크립터는 서브 프로세스에 의해 상속됩니다.
  • 파이프는 항상 파일 디스크립터 (한 쌍), 경로 재 지정을 연결합니다. 경로 이름 또는 파일 디스크립터를 사용하십시오.
  • 파이프는 프로세스 간 통신 방법이며 리디렉션은 열린 파일이나 파일과 같은 개체에 대한 조작 일뿐입니다.
  • 둘 다 dup2()실제 데이터 흐름이 발생하는 파일 디스크립터의 사본을 제공하기 위해 후드 아래에 syscall을 사용 합니다.
  • exec기본 제공 명령을 사용 하여 리디렉션을 "전역 적으로"적용 할 수 있으므로 ( thisthis 참조 ) exec > output.txt모든 명령 을 수행하는 경우 output.txt그 이후부터 쓰게됩니다 . |파이프는 현재 명령 (단순 명령 또는 서브 쉘 유사 seq 5 | (head -n1; head -n2)또는 복합 명령 을 의미)에만 적용됩니다 .
  • 파일에 대한 경로 재 지정이 수행되면 해당 파일에서 syscall을 사용 echo "TEST" > file하고 ( 또한 참조 ) 파일 디스크립터를 가져 와서 파일 디스크립터로 전달하십시오 . 파이프 만을 사용 하고 콜.echo "TEST" >> fileopen()dup2()|pipe()dup2()

  • 명령이 실행되는 한 파이프와 리디렉션은 맹목적으로 쓰거나 내부적으로 조작 할 수있는 파일과 같은 파일 설명자입니다. 예 apt를 들어 stdout에 쓰지 않는 경향이 있습니다. 리디렉션이 있다는 것을 알고 있다면).

소개

이 두 메커니즘의 차이점을 이해하려면 필수 속성, 두 가지 역사 및 C 프로그래밍 언어의 근본을 이해해야합니다. 실제로, 파일 디스크립터가 무엇인지, dup2()그리고 pipe()시스템 호출이 어떻게 작동 하고 어떻게 작동 하는지 아는 것이 필수적 lseek()입니다. 쉘은 이러한 메커니즘을 사용자에게 추상적으로 만드는 방법을 의미하지만 추상화보다 더 깊이 파고 들어가면 쉘 동작의 실제 특성을 이해하는 데 도움이됩니다.

리디렉션 및 파이프의 기원

데니스 리치의 기사에 따르면 예언 암각화 , 파이프는 유래 1964 내부 메모 에 의해 말콤 더글라스 맥 일로이 가 작업을 한 번에, 멀 틱스 운영 체제 . 인용문:

내 가장 큰 관심사를 간단히 말하면 :

  1. 정원 호스와 같은 프로그램을 연결하는 방법이 있어야합니다. 다른 방법으로 데이터를 마사지해야 할 때 다른 세그먼트의 나사가 필요합니다. 이것은 또한 IO의 방법입니다.

명백한 것은 프로그램이 디스크에 쓸 수 있었지만 출력이 클 경우 비효율적 이었다는 것입니다. 유닉스 파이프 라인 비디오 에서 Brian Kernighan의 설명을 인용하려면 :

첫째, 하나의 큰 대규모 프로그램을 작성할 필요가 없습니다. 이미 작업의 일부를 수행 할 수있는 기존의 작은 프로그램이 있습니다 ... 또 다른 방법은 처리중인 데이터의 양이 적합하지 않을 수 있다는 것입니다 운이 좋으면 메가 바이트 또는 두 개의 데이터가있는 파이프 라인이 있었기 때문에 파이프 라인이 전체 출력을 인스턴스화 할 필요가 없었던 것을 기억하십시오. .

따라서 개념적 차이는 명백합니다. 파이프는 프로그램이 서로 대화하게하는 메커니즘입니다. 리디렉션-기본 수준에서 파일에 쓰는 방법입니다. 두 경우 모두 쉘은이 두 가지를 쉽게 해주지 만, 후드 아래에는 많은 일들이 있습니다.

심층 분석 : 쉘의 syscall 및 내부 작업

우리는 파일 디스크립터 의 개념으로 시작 합니다 . 파일 디스크립터는 기본적으로 열린 파일 (디스크 또는 메모리에있는 파일이든 익명 파일이든)을 정수로 표시합니다. 두 개의 표준 데이터 스트림 (stdin, stdout, stderr)은 각각 파일 디스크립터 0, 1 및 2입니다. 그들은 어디에서 왔니 ? 셸 명령에서 파일 디스크립터는 부모-셸에서 상속됩니다. 자식 프로세스는 부모의 파일 디스크립터를 상속합니다. 들어 데몬 은 상속 된 모든 파일 디스크립터를 닫고 / 또는 다른 장소로 리디렉션하는 것이 일반적이다.

리디렉션으로 돌아 가기 정말 무엇입니까? 쉘이 명령을 실행하기 전에 쉘에 의해 경로 재 지정이 수행되기 때문에 명령에 대한 파일 디스크립터를 준비하고 사용자가 제안한 위치를 지시하도록하는 메커니즘입니다. 출력 리디렉션 의 표준 정의

[n]>word

그건 [n]파일 기술자 번호가있다. 당신이 할 때 echo "Something" > /dev/null1 번 숫자가 암시 echo 2> /dev/null됩니다.

후드 아래에서 이것은 dup2()시스템 호출을 통해 파일 디스크립터를 복제하여 수행됩니다 . 하자 df > /dev/null. 쉘은 자식 프로세스 생성됩니다 df실행을하지만, 그 전에이 열립니다 /dev/null파일 기술자 # 3으로하고, dup2(3,1)파일 기술자 3의 복사본을 만드는 발행되고 복사 당신은 당신이 두 개의 파일이 방법을 알고 1. 될 것입니다 file1.txtfile2.txt그리고 cp file1.txt file2.txt두 개의 동일한 파일이 있지만 독립적으로 조작 할 수 있습니까? 그것은 여기서 일어나는 것과 똑같은 일입니다. 종종 당신은 실행하기 전에이 있음을 볼 수있다 bash할 것 dup(1,10)입니다 복사 파일 기술자 # 1을 만들기 위해 stdout(그 사본 # 10 FD 것) 나중에 복원하기 위해. 내장 명령 을 고려할 때 중요 합니다.(쉘 자체의 일부이며 파일이 /bin없거나 다른 곳에 파일이 없음 ) 또는 비 대화식 shell에 간단한 명령 인 경우, 쉘은 자식 프로세스를 생성하지 않습니다.

그리고 우리는 같은 일을 [n]>&[m]하고 [n]&<[m]. 이것은 파일 디스크립터를 복제하는 것인데, 현재와 동일한 메커니즘 dup2()이 쉘 구문에 있으며 사용자가 편리하게 사용할 수 있습니다.

리디렉션에 대해주의해야 할 중요한 사항 중 하나는 순서가 고정되어 있지 않지만 쉘이 사용자가 원하는 것을 해석하는 방법에 중요하다는 것입니다. 다음을 비교하십시오.

# Make copy of where fd 2 points , then redirect fd 2
$ ls -l /proc/self/fd/  3>&2  2> /dev/null
total 0
lrwx------ 1 user user 64 Sep 13 00:08 0 -> /dev/pts/0
lrwx------ 1 user user 64 Sep 13 00:08 1 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:08 2 -> /dev/null
lrwx------ 1 runner user 64 Sep 13 00:08 3 -> /dev/pts/0
lr-x------ 1 user user 64 Sep 13 00:08 4 -> /proc/29/fd

# redirect fd #2 first, then clone it
$ ls -l /proc/self/fd/    2> /dev/null 3>&2
total 0
lrwx------ 1 user user 64 Sep 13 00:08 0 -> /dev/pts/0
lrwx------ 1 user user 64 Sep 13 00:08 1 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:08 2 -> /dev/null
l-wx------ 1 user user 64 Sep 13 00:08 3 -> /dev/null
lr-x------ 1 user user 64 Sep 13 00:08 4 -> /proc/31/fd

쉘 스크립팅에서 이들을 실용적으로 사용할 수 있습니다.

그리고 다른 많은.

와 배관 pipe()dup2()

파이프는 어떻게 만들어 집니까? pipe()syscall을 통해 입력 pipefd의 두 항목 유형 int(정수)으로 불리는 배열 (일명 목록) 을 입력으로 받습니다. 이 두 정수는 파일 설명자입니다. 는 pipefd[0]파이프의 읽기 끝이하고 pipefd[1]쓰기 종료 될 것입니다. 그래서에서 df | grep 'foo', grep사본을 얻을 것이다 pipefd[0]df의 사본을 얻을 것이다 pipefd[1]. 그러나 어떻게? 물론 dup2()syscall 의 마법으로 . 이 df예제에서 pipefd[1]# 4 가 있다고 가정 해 봅시다 . 그래서 쉘이 자식을 만들고 dup2(4,1)(내 cp예제를 기억 하십니까?) execve()실제로 실행 df합니다. 당연히,df파일 디스크립터 # 1을 상속 받지만 더 이상 터미널을 가리 키지 않지만 실제로 파이프의 쓰기 끝인 fd # 4를 인식하지 못합니다. 당연히 grep 'foo'다른 수의 파일 디스크립터 를 제외하고 는 동일한 일이 발생 합니다.

이제 흥미로운 질문 : fd # 1뿐만 아니라 fd # 2를 리디렉션하는 파이프를 만들 수 있습니까? 예, 사실 |&bash에서 수행하는 작업입니다. POSIX 표준을 지원하는 쉘 명령 언어를 필요로 df 2>&1 | grep 'foo'그 목적을 위해 구문을하지만, bash않습니다 |&뿐만 아니라.

중요한 것은 파이프는 항상 파일 디스크립터를 처리한다는 것입니다. 디스크에 파일 이름이 있고 파일로 사용하지만 파이프처럼 동작하는 pipe 가 존재 FIFO하거나 이름이 지정 됩니다. 그러나 |파이프 유형은 익명 파이프로 알려져 있습니다. 파일 이름이 없습니다. 실제로는 두 개체가 서로 연결되어 있기 때문입니다. 우리가 파일을 다루지 않는다는 사실도 중요한 의미를 갖습니다. 파이프는 불가능 lseek()합니다. 메모리 나 디스크에있는 파일은 정적입니다. 프로그램은 lseek()syscall을 사용 하여 바이트 120으로 건너 뛰고 바이트 10으로 되돌아 간 다음 끝까지 계속 전달할 수 있습니다. 파이프는 정적이 아닙니다. 파이프는 순차적이므로 파이프에서 얻은 데이터를 되 감을 수 없습니다lseek(). 파일이나 파이프에서 읽을 때 일부 프로그램이이를 인식하게하므로 효율적인 성능을 위해 필요한 조정을 수행 할 수 있습니다. 즉, 또는 prog할 경우를 감지 할 수 있습니다 . 그 실제 사례는 tail 입니다.cat file.txt | progprog < input.txt

파이프의 다른 두 가지 흥미로운 속성은 리눅스에서 4096 바이트 인 버퍼를 가지고 있고 실제로 리눅스 소스 코드에서 정의 된 파일 시스템을 가지고 있다는 것입니다 ! 그들은 단순히 데이터를 전달하는 객체가 아니라 데이터 구조 자체입니다! 실제로 파이프와 FIFO를 모두 관리하는 pipefs 파일 시스템이 있으므로 파이프는 해당 파일 시스템에 inode 번호가 있습니다.

# Stdout of ls is wired to pipe
$ ls -l /proc/self/fd/  | cat  
lrwx------ 1 user user 64 Sep 13 00:02 0 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:02 1 -> pipe:[15655630]
lrwx------ 1 user user 64 Sep 13 00:02 2 -> /dev/pts/0
lr-x------ 1 user user 64 Sep 13 00:02 3 -> /proc/22/fd
# stdin of ls is wired to pipe
$ true | ls -l /proc/self/fd/0
lr-x------ 1 user user 64 Sep 13 03:58 /proc/self/fd/0 -> 'pipe:[54741]'

Linux 파이프는 리디렉션과 마찬가지로 단방향입니다. 일부 유닉스 계열 구현에는 양방향 파이프가 있습니다. 쉘 스크립팅의 마법이 있지만 Linux 에서도 양방향 파이프를 만들 수 있습니다.

또한보십시오:


2

다른 답변에 추가하기 위해 미묘한 의미 론적 차이도 있습니다. 예를 들어 파이프가 리디렉션보다 더 가깝게 닫힙니다.

seq 5 | (head -n1; head -n1)                # just 1
seq 5 > tmp5; (head -n1; head -n1) < tmp5   # 1 and 2
seq 5 | (read LINE; echo $LINE; head -n1)   # 1 and 2

첫 번째 예제에서 첫 번째 호출이 head완료되면 파이프가 닫히고 seq종료되므로 두 번째에 사용할 수있는 입력이 없습니다 head.

두 번째 예에서 head는 첫 번째 행을 사용하지만 자체 stdin 파이프를 닫으면 다음 호출에서 사용할 수 있도록 파일이 열린 상태로 유지됩니다.

세 번째 예는 read파이프 폐쇄를 피하기 위해 사용하는 경우 하위 프로세스 내에서 여전히 파이프를 사용할 수 있음을 보여줍니다 .

따라서 "스트림"은 데이터를 분류하고 (stdin 등) 두 경우 모두 동일하지만 파이프는 두 프로세스의 스트림을 연결합니다. 여기서 리디렉션은 프로세스와 파일 사이의 스트림을 연결합니다. 유사점과 차이점의 근원을 볼 수 있습니다.

추신. 당신이 그 예에 대해 궁금하거나 놀랐다면 trap, 프로세스가 어떻게 해결되는지보기 위해 더 깊이 파고들 수 있습니다 .

(trap 'echo seq EXITed >&2' EXIT; seq 5) | (trap 'echo all done' EXIT; (trap 'echo first head exited' EXIT; head -n1)
echo '.'
(trap 'echo second head exited' EXIT; head -n1))

때로는 첫 번째 프로세스가 닫히기 전에 닫히기 1도합니다.

또한 exec <&-파이프 동작을 근사하기 위해 리디렉션에서 스트림을 닫는 데 사용 하는 것이 흥미로 웠습니다 (오류가 있음).

seq 5 > tmp5
(trap 'echo all done' EXIT
(trap 'echo first head exited' EXIT; head -n1)
echo '.'
exec <&-
(trap 'echo second head exited' EXIT; head -n1)) < tmp5`

"헤드에 대한 첫 번째 호출이 완료되면 파이프가 닫힙니다"이것은 실제로 두 가지 이유로 부정확합니다. 하나 (head -n1; head -n1)는 두 개의 명령이있는 서브 쉘이며, 각 명령은 파이프의 읽기 끝을 설명자 0으로 상속하므로 서브 쉘 AND 각 명령은 해당 파일 설명자를 엽니 다. 두 번째 이유는 strace -f bash -c 'seq 5 | (head -n1; head -n1) '입니다. 첫 번째 헤드는 파일 디스크립터의 사본 만 닫습니다
Sergiy Kolodyazhnyy

read첫 번째 줄만 사용 하기 때문에 세 번째 예제도 정확하지 않습니다 (1 바이트 for 1및 newline). seq총 10 바이트 (5 개의 숫자와 5 개의 줄 바꿈)로 전송됩니다. 따라서 파이프 버퍼에 8 바이트가 남아 있으므로 이것이 두 번째로 head작동하는 이유 입니다. 파이프 버퍼에 여전히 사용 가능한 데이터가 있습니다. Btw, 0 바이트를 읽은 경우에만 헤드가 종료됩니다.head /dev/null
Sergiy Kolodyazhnyy

설명해 주셔서 감사합니다. seq 5 | (head -n1; head -n1)첫 번째 호출에서 파이프가 비워 지므로 파이프가 여전히 열린 상태이지만 두 번째 호출에 대한 데이터는 없음을 올바르게 이해 하고 head있습니까? 파이프와 경로 재 지정의 동작의 차이는 헤드가 파이프에서 모든 데이터를 가져 오지만 파일 핸들에서 두 줄만 가져 오기 때문입니다.
Julian de Bhal

맞습니다. 그리고 그것은 strace첫 번째 의견에서 내가 준 명령 으로 볼 수있는 것입니다 . 리디렉션을 사용하면 tmp 파일이 디스크에있어 탐색 가능합니다 ( lseek()syscall 을 사용하기 때문에 -명령은 파일을 첫 번째 바이트에서 마지막 바이트까지 이동할 수 있지만 파이프는 순차적이며 탐색 할 수 없습니다. 따라서 헤드를 수행하는 유일한 방법은 작업은 먼저 모든 것을 읽을 수있다, 또는 파일이 큰 경우 -을 통해 RAM에 그 중 일부를 매핑 mmap()전화 나는 한 번 내 자신했다. tail파이썬에서, 정확히 같은 문제로 실행했습니다.
세르지 Kolodyazhnyy

파이프의 읽기 끝 (파일 디스크립터)이 먼저 서브 쉘에 제공되고 서브 (...)쉘이 내부의 각 명령에 대한 자체 stdin의 사본을 작성 한다는 점을 기억하는 것이 중요합니다 (...). 따라서 기술적으로 동일한 객체에서 읽습니다. 먼저 head 자체 stdin에서 읽는다고 생각합니다. 두 번째 head는 자체 stdin이 있다고 생각합니다. 그러나 실제로 fd # 1 (stdin)은 동일한 fd의 복사본이며 파이프 끝에서 읽습니다. 또한 답변을 게시 했으므로 내용을 명확히하는 데 도움이 될 수 있습니다.
Sergiy Kolodyazhnyy

1

나는 오늘 C에서 이것에 대한 문제를 겪었다. 본질적으로 파이프는로 보낼 때도 리디렉션에 대한 의미가 다릅니다 stdin. 실제로 차이점을 감안할 때 파이프가 아닌 다른 곳으로 가야 stdin하므로 임의의 차등을 만들기 위해 stdin호출 할 stdpipe수 있습니다.

이걸 고려하세요. 파일이 있음에도 불구하고 한 프로그램 출력을 다른 프로그램으로 파이핑하면 fstat0을 반환하는 것처럼 보입니다 . 파일을 리디렉션 할 때 적어도 데비안의 경우 (아니다 , 그리고 바닐라와 우분투 , 바닐라.st_sizels -lha /proc/{PID}/fdwheezystretchjessie14.0416.04

당신이 경우 cat /proc/{PID}/fd/0리디렉션에 당신은 당신이 원하는만큼 여러 번 읽기를 반복 할 수 있습니다. 파이프로이 작업을 수행하면 두 번째로 작업을 연속으로 실행할 때 동일한 출력을 얻지 못합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.