파이프, {목록; } 일부 프로그램에서만 작동 SZ

이러한 예측할 수없는 동작에 대해서는 고급 사용자의 설명이 필요합니다.

ps -eF | { head -n 1;grep worker; }
UID        PID  PPID  C    SZ   RSS PSR STIME TTY          TIME CMD
root       441     2  0     0     0   2 paź15 ?       00:00:00 [kworker/2:1H]

모든 것이 정상으로 보이지만

ls -la / | { head -n 1;grep sbin; }

의 출력 만 표시합니다 head

… 나는 생각 stdout 2>&1하고 나에게도 효과가 없거나 이상하지 않습니다. 설명이나 처리 방법을 제안합니까?



답변

나는 조사 중 일부를 사용 strace했으며 파이프 라인의 왼쪽에있는 프로그램이 터미널에 쓰는 방식으로 인한 것 같습니다. 하면 ls명령이 실행 그것은 하나에 모든 데이터를 기록합니다 write(). 이로 인해 head모든 stdin이 소비됩니다.

반면에 ps데이터를 일괄 적 으로 기록하므로 첫 번째 만 write()소비 하고 데이터 head가 존재합니다. 나중에 전화 write()하면 새로 생성 된 grep프로세스 로 이동합니다 .

즉 , 모든 데이터를 볼 수 없으므로 (첫 번째 행에서 데이터를 뺀 것보다 훨씬 적습니다) 프로세스 grep가 첫 번째 프로세스 에서 발생 하지 않으면 작동하지 않습니다.write()grep

내 시스템에서 pid 1을 grep하려는 예는 다음과 같습니다.

$ ps -eF | { head -n2; }
UID        PID  PPID  C    SZ   RSS PSR STIME TTY          TIME CMD
root         1     0  0  1697  3768   2 Oct03 ?        00:00:03 /lib/systemd/systemd
$ ps -eF | grep '/lib/systemd/systemd$'
root         1     0  0  1697  3768   2 Oct03 ?        00:00:03 /lib/systemd/systemd
$ ps -eF | { head -n1; grep '/lib/systemd/systemd$'; }
UID        PID  PPID  C    SZ   RSS PSR STIME TTY          TIME CMD

귀하의 ps -eF예는 우연히 만 작동합니다.


답변

이것은 glibc의 버퍼링으로 인해 발생합니다. ls출력이 하나의 내부 버퍼에 있는 경우로 전달됩니다 head. 의 경우 ps -eF출력이 더 크므로 head완료되면 다음 grep의 나머지 부분 (전체는 아님)이 출력 ps됩니다.

파이프를 버퍼링 해제하여 제거 할 수 있습니다 sed -u( 예 : (GNU 확장이 아닌지 확실하지 않습니다)).

$ ls -al / | sed -u "#" | { head -n 1; grep bin; }
total 76
drwxr-xr-x   2 root root  4096 Oct  2 21:52 bin
drwxr-xr-x   2 root root  8192 Oct  3 01:54 sbin


답변

무슨 일이 일어나고 있는지 한 head -n 1줄 이상 읽습니다. 최적의 처리량을 위해 head는 바이트 단위를 읽으므로 한 번에 1024 바이트를 읽은 다음 첫 번째 줄 바꿈을 위해 해당 바이트를 살펴볼 수 있습니다. 1024 바이트 중간에 줄 바꿈이 발생할 수 있으므로 나머지 데이터는 손실됩니다. 파이프에 다시 넣을 수 없습니다. 따라서 다음 실행 프로세스는 바이트 1025 이상 만 가져옵니다.

kworker프로세스는 head읽은 첫 번째 청크 이후 프로세스 이기 때문에 첫 번째 명령이 성공합니다 .

이것이 작동하려면 head한 번에 1 문자를 읽어야합니다. 그러나 이것은 매우 느리므로 그렇게하지 않습니다.
이와 같은 작업을 효율적으로 수행하는 유일한 방법은 단일 프로세스가 “헤드”와 “그렙”을 모두 수행하는 것입니다.

이 작업을 수행하는 두 가지 방법이 있습니다.

echo -e '1\n2\n3\n4\n5' | perl -ne 'print if $i++ == 0 || /4/'

또는

echo -e '1\n2\n3\n4\n5' | awk '{if (NR == 1 || /4/) print }'

더 많은 것이 있습니다 …


답변

첫 번째 또는 두 번째 행만 원하는 경우 다음 유형의 트릭이 작동하고 두 개의 다른 명령을 사용하여 출력 스트림을 읽음으로써 발생하는 버퍼링 문제를 피할 수 있습니다.

$ ps -eF   | { IFS= read -r x ; echo "$x" ; grep worker; }
$ ls -la / | { IFS= read -r x ; echo "$x" ; grep sbin; }

read쉘에 내장되어 있으므로 사용하여 바로 출력 한 줄에 입력의 전체 버퍼를 소비하지 않는 read다음 명령 잎을 출력의 모든 나머지를.

두 개의 다른 명령을 사용하는 예제에 표시된 버퍼링 문제를 강조하려면 sleep타이밍 문제를 제거하고 오른쪽의 명령이 다음 중 하나를 읽으려고 시도하기 전에 왼쪽의 명령이 모든 출력을 생성하도록 허용하십시오. 그것:

$ ps -eF   | { sleep 5 ; head -n 1 ; grep worker; }
$ ls -la / | { sleep 5 ; head -n 1 ; grep sbin; }

이제 위의 두 예제 모두 같은 방식으로 실패 head합니다. 출력의 전체 버퍼를 읽고 한 줄만 생성하면 해당 버퍼를 다음에 사용할 수 없습니다 grep.

출력 라인의 번호를 지정하는 몇 가지 예를 사용하여 버퍼링 문제를보다 명확하게 알 수 있으므로 누락 된 라인을 알 수 있습니다.

$ ps -eF          | cat -n | { sleep 5 ; head -n 1 ; head ; }
$ ls -la /usr/bin | cat -n | { sleep 5 ; head -n 1 ; head ; }

버퍼링 문제를 보는 간단한 방법 seq은 숫자 목록을 생성하는 것입니다. 어떤 숫자가 빠졌는지 쉽게 알 수 있습니다.

$ seq 1 100000    | { sleep 5 ; head -n 1 ; head ; }
1

1861
1862
1863
1864
1865
1866
1867
1868
1869

쉘을 사용하여 첫 번째 줄을 읽고 에코하는 트릭 솔루션은 수면 지연이 추가 된 경우에도 올바르게 작동합니다.

$ seq 1 100000 | { sleep 5 ; IFS= read -r x ; echo "$x" ; head ; }
1
2
3
4
5
6
7
8
9
10
11

아래는 head버퍼링 문제 를 보여주는 전체 예제 head이며, 매번 5 개의 라인을 생성하기 위해 출력의 전체 버퍼를 소비하는 방법을 보여줍니다
. head순서대로 다음 명령에서 사용 된 버퍼를 사용할 수 없습니다
.

$ seq 1 100000 | { sleep 5 ; head -5 ; head -5 ; head -5 ; head -5 ; }
1
2
3
4
5

1861
1862
1863
1864
499
3500
3501
3502
3503
7
5138
5139
5140
5141

번호를 찾고 1861상기 우리는 버퍼의 크기가 사용되고 계산할 수 head카운트하여 seq출력을 1
1860:

$ seq 1 1860 | wc -c
8193

우리 head는 파이프 출력의 전체 8KB (8 * 1024 바이트)를 한 번에 읽고 심지어 몇 줄의 자체 출력을 생성하여 버퍼링하는 것을 볼 수 있습니다.


답변