답변:
파이프는 커널 내부 파일 시스템에서 열린 파일이며 디스크의 일반 파일로 액세스 할 수 없습니다. 특정 크기로만 자동 버퍼링되며 가득 차면 결국 차단됩니다. 블록 장치에서 제공되는 파일과 달리 파이프는 문자 장치와 매우 유사하게 작동하므로 일반적으로 지원하지 않으며 파일에서 lseek()
읽은 데이터는 일반 파일에서와 같이 다시 읽을 수 없습니다.
here-string은 마운트 된 파일 시스템에서 생성 된 일반 파일입니다. 쉘은 파일을 생성하고 즉시 전용 파일 시스템 링크를 제거하는 동안 그 설명을 유지 (그래서 그것을 삭제) 전에 이제까지는 기록 / 파일에서 /에 바이트를 읽습니다. 커널은 모든 프로세스가 파일에 대한 모든 설명자를 해제 할 때까지 파일에 필요한 공간을 유지합니다. 그러한 디스크립터에서 읽는 자식이 그렇게 할 수있는 능력이 있다면, 다시 감고 lseek()
다시 읽을 수 있습니다 .
두 경우 모두 토큰 <<<
과 |
파일 설명자를 나타내며 반드시 파일 자체는 아닙니다. 다음과 같은 작업을 수행하여 진행 상황을 더 잘 이해할 수 있습니다.
readlink /dev/fd/1 | cat
...또는...
ls -l <<<'' /dev/fd/*
두 파일의 가장 큰 차이점은 here-string / doc이 거의 한 번에 발생하는 일이라는 점입니다. 셸은 읽기 설명자를 자식에게 제공하기 전에 모든 데이터를 기록합니다. 반면에, 쉘은 적절한 디스크립터에서 파이프를 열고 파이프에 대한 디스크립터를 관리하기 위해 하위를 포크 오프하므로 양쪽 끝에서 동시에 쓰거나 읽습니다 .
그러나 이러한 차이점은 일반적으로 사실입니다. 내가 아는 한 (실제로는 그렇지는 않습니다) 이것은 단일 예외를 가진 here-document redirection에 <<<
대한 here-string short-hand를 처리하는 거의 모든 셸에서 사실 <<
입니다 yash
. yash
, busybox
, dash
, 기타 및 ash
변형, 그래서 그 껍질에 정말 결국 둘 사이에 약간의 차이가 있지만, 파이프 여기-문서를 백업하는 경향이있다.
확인-두 가지 예외가 있습니다. 이제 그것에 대해 생각하고 ksh93
실제로 파이프를 전혀 사용 |
하지 않지만 소켓을 사용하여 전체 비즈니스를 처리합니다 <<<*
. 대부분의 다른 사람들처럼 삭제 된 tmp 파일을 수행합니다. 또한 파이프 라인의 별도 섹션을 서브 쉘 환경에 배치합니다.이 쉘 은 적어도 서브 쉘과 같은 역할을 하기 때문에 일종의 POSIX 완곡 어이며 포크조차하지 않습니다.
사실 @PSkocik의 벤치 마크 (매우 유용한) 결과는 여러 가지 이유로 광범위하게 달라질 수 있으며, 대부분은 구현에 따라 다릅니다. 여기서 문서 설정의 가장 큰 요인은 대상 ${TMPDIR}
파일 시스템 유형과 현재 캐시 구성 / 가용성 및 더 많은 데이터를 쓸 것입니다. 파이프의 경우 필요한 포크에 대해 사본이 작성되므로 쉘 프로세스 자체의 크기가됩니다. 이 방법에서 bash
입니다 끔찍한 파이프 라인 설치에 (포함하는 $(
명령 )
대체를) - 그것은 크고 있기 때문에 매우 느린, 그러나와 ksh93
는 전혀 거의 차이가 있습니다.
다음은 쉘이 파이프 라인의 서브 쉘을 분리하는 방법을 보여주는 또 다른 작은 쉘 스 니펫입니다.
pipe_who(){ echo "$$"; sh -c 'echo "$PPID"'; }
pipe_who
pipe_who | { pipe_who | cat /dev/fd/3 -; } 3<&0
32059 #bash's pid
32059 #sh's ppid
32059 #1st subshell's $$
32111 #1st subshell sh's ppid
32059 #2cd subshell's $$
32114 #2cd subshell sh's ppid
파이프 라인 된 pipe_who()
호출이보고하는 것과 현재 셸에서 한 실행에 대한보고의 차이점 은 확장 될 때 부모 셸의 pid를 주장 하는 (
서브 쉘의 )
지정된 동작 $$
때문입니다. 하지만 bash
서브 쉘은 확실히 별도의 프로세스가의 $$
특별한 쉘 매개 변수는이 정보의 신뢰할 수있는 원본이 아니다. 그럼에도 불구하고 서브 쉘의 자식 sh
쉘은이를 정확하게보고하지 않습니다 $PPID
.
벤치마킹을 대체 할 수있는 것은 없습니다 :
pskocik@ProBook:~
$ time (for((i=0;i<1000;i++)); do cat<<< foo >/dev/null; done )
real 0m2.080s
user 0m0.738s
sys 0m1.439s
pskocik@ProBook:~
$ time (for((i=0;i<1000;i++)); do echo foo |cat >/dev/null; done )
real 0m4.432s
user 0m2.095s
sys 0m3.927s
$ time (for((i=0;i<1000;i++)); do cat <(echo foo) >/dev/null; done )
real 0m3.380s
user 0m1.121s
sys 0m3.423s
그리고 더 많은 양의 데이터의 경우 :
TENMEG=$(ruby -e 'puts "A"*(10*1024*1024)')
pskocik@ProBook:~
$ time (for((i=0;i<100;i++)); do echo "$TENMEG" |cat >/dev/null; done )
real 0m42.327s
user 0m38.591s
sys 0m4.226s
pskocik@ProBook:~
$ time (for((i=0;i<100;i++)); do cat<<< "$TENMEG" >/dev/null; done )
real 1m26.946s
user 1m23.116s
sys 0m3.681s
pskocik@ProBook:~
$ time (for((i=0;i<100;i++)); do cat <(echo "$TENMEG") >/dev/null; done )
real 0m43.910s
user 0m40.178s
sys 0m4.119s
파이프 버전은 설치 비용이 더 크지 만 결국 더 효율적입니다.
echo foo >/dev/shm/1;cat /dev/shm/1 >/dev/null
두 경우 모두에 빠른 것처럼 보였다 ...
echo "$longstring"
하거나 <<<"$longstring"
조정 해야하는 이유를 모르겠습니다 . 어쨌든 효율성은별로 중요하지 않습니다.
cat <(echo foo) >/dev/null
빠릅니다 echo foo | cat >/dev/null
.