Wi-Fi 안테나가 있는데 그 위치를 최적화하고 싶습니다. 이 제안에서는 현재 대기 시간을 실시간으로 시각화하고 싶습니다.
핑 대기 시간을 출력하기 위해 한 줄짜리 스크립트를 작성했습니다.
위도:
ping 8.8.8.8|gawk '/64 bytes/{ match($7,/[0-9.]+/,arr); print((i++)," ",arr[0]);}'
gnuplot을 사용하여 플롯하고 싶습니다.
plot "<lat.sh"
그건 작동하지 않습니다. lat.sh가 터미널에 표시해야 할 내용을 표시한다는 것을 알았습니다. 예를 들면 다음과 같습니다.
$ ./lat.sh
0 47.7
1 25.5
2 15.8
3 16.7
그러나 파이프로 출력하는 것은 작동하지 않습니다. ./lat.sh > outfile
또는 터미널에 아무것도 인쇄되지 않지만 파일(또는 파이프)은 비어 있습니다 ./lat.sh|tee outfile
. ./lat.sh &> outfile
gnuplot 파이프라인도 마찬가지입니다. 정말 혼란스러워요.
답변1
맥셀프처럼설명하다아래는 버퍼링 문제입니다. 이 문제를 해결하는 한 가지 방법은 awk
즉시 사용을 강제하는 것입니다 fflush()
(나머지는 현재 수행 중인 작업을 단순화한 버전입니다).
ping 8.8.8.8 | gawk -F'[= ]' '/^64/{print i++,$(NF-1); fflush()}'
이 호출은 버퍼가 가득 찰 때까지 기다리지 않고 출력이 가능해지면 즉시 출력되도록 fflush()
합니다 .awk
또는 ping
특정 횟수만큼 실행해 볼 수도 있습니다. 예를 들어 10:
ping -c 10 8.8.8.8 | gawk ...
답변2
문제는 여기에 있는 것 같습니다 awk
. 프로그램에서 출력이 생략되었습니다. 잘라내세요. 이는 매우 유용할 수 있지만 이렇게 하면 대기 중인 출력이 줄어듭니다 awk
. 대부분의 UNIX 프로그램에서는stdio.h (이것은 대부분의 UNIX 프로그램입니다)출력 장치의 출력은 해당 유형에 따라 버퍼링됩니다. 출력 장치가 터미널인 경우 대부분의 UNIX 프로그램은 출력을 라인 버퍼링하고 write()
한 줄에 한 번씩 실행합니다. 언제아니요하지만,(파이프일 때처럼)프로그램은 쓰기당 정의된 바이트 수를 버퍼링합니다.
이는 일반적으로 표준 동작입니다.좋아요물건. 모든 호출 write()
은 시스템 호출입니다. 이는 시스템 커널의 핵심 기능과 가장 기본적인 기능에 대한 연결입니다. 유닉스 시스템은시간 공유- 커널은 필요에 따라 각 요청 프로세스에 CPU 처리 시간을 할당합니다. 그래서지불하다효율성을 높이고 I/O를 관리 가능한 덩어리로 나눕니다. 이것이 바로 애플리케이션이 버퍼링되는 이유입니다.
이것이 awk
우리가 하고 있는 일입니다. 문제는 입력 필터링으로 인해 자체 출력이 초당 약 8~10바이트로 감소한다는 사실로 인해 더욱 복잡해집니다. awk
출력이 4kb 청크로 버퍼링되는 경우(이건 흔한 일이다), 초당 약 10바이트의 출력만 생성하므로 6분에 한 번만 쓰게 됩니다. 대륙 표류 규모의 특정 주파수를 조사하지 않는 한 지연을 측정하는 데 그다지 유용한 응용 프로그램이 되지 않습니다.
이 문제를 처리하는 몇 가지 방법이 있습니다. 예를 들어 파이프를 pty로 감쌀 수 있습니다. screen
이는 사용자 및 기타 여러 응용 프로그램에서 수행할 수 있습니다. 이를 처리하는 -native 옵션 이 있을 수 있지만 awk
잘 모르겠습니다.(일지라도테든은 반드시 그럴 것이다). GNU 도구는 stdbuf
프로그램이 실행되는 동안 libc 호출을 주입하여 출력 버퍼를 설정할 수 있습니다. 이는 일반적으로 매우 잘 작동하지만 나중에 실행되는 프로그램이 실행 중에 버퍼를 조정하는 경우에는 그다지 유용하지 않습니다.
awk
지금은 비난 하고 있지만 ping
출력을 버퍼링하는 것도 가능합니다. 그러나 적어도 내 Linux 시스템에서는 다음과 같이 호출합니다.
ping -O -n 8.8.8.8 | ...
...그렇지 않습니다. 궁금하신 분들을 위해 표준 UNIX 명령에 대해 매우 기본적인 POSIX 옵션만 사용하여 사용 가능한 모든 출력 행을 일관되게 작성하도록 했습니다.
제일 쉬운:
ping -On 8.8.8.8 |
sed -u 's/^64.*=\(.*\) ttl.*=/\1 /' |
more pipeline
...GNU, BSD 또는 AST와 함께 사용됩니다 sed
. 게다가...
ping -On 8.8.8.8 |
sed 's/^64.*=\(.*\) ttl.*=/\1 /w target_file' |
more pipeline
...가능한 처리 후에 모든 입력 행을 표준 출력으로 복사합니다. 이는 블록 버퍼링될 가능성이 높습니다. 그러나 그것은반품w
각각 의 성공적인 교체 결과를 즉시 다음 위치에 기록합니다.s///
대상 파일. w
지정된 모든 의식 파일에 대한 즉각적인 의식은 w
POSIX에서 지정한 sed
동작입니다.(거의)r
ead 파일 도 마찬가지입니다. 테마를 약간 변형하면 다음과 같습니다.
ping -On 8.8.8.8 |
sed 's/^64.*=\(.*\) ttl.*=/\1 /w /dev/fd/1' |
more pipeline
링크 주소가 지정된 파일 설명자 사용을 지원하는 시스템에서 /dev
위의 내용은 적어도 현재로서는 표준 쉘 파이프를 통해 라인 버퍼 출력을 생성해야 합니다 sed
. 이는 직접 연결을 지원하지 않는 시스템 , 심지어 구형 시스템에서도 여전히 널리 사용되는 처리 기능 /dev/fd
입니다 . 기본 stdout 출력을 완전히 비활성화 하는 데 사용됩니다 .sed
/dev/std(in|out|err)
minised
-n
다음은 이 구성을 기반으로 하는 보다 유연하고 복잡한 솔루션입니다.
mkfifo /tmp/ipipe /tmp/opipe
exec 9<>/tmp/opipe 8<>/tmp/ipipe
trap ' printf \\nTTY:STOP\\n >&8' INT
sed -n 's/^64.*=\(.*\) ttl.*=/\1\t/
/^TTY:START$/,/^TTY:STOP$/{
/^TTY/d;w /dev/tty
}; /^PIP:START$/,/^PIP:STOP$/{
/^PIP/d;w /tmp/opipe
}' <&8 >/dev/null 2>&1 & SEDPID=$!
ping -On 8.8.8.8 >&8 2>&8 & PNGPID=$!
printf \\nPIP:START\\n >&8;rm /tmp/?pipe
이제 항상 라인 버퍼 방식으로 출력을 기록하는 백그라운드 스케줄러 프로세스가 설정되었습니다. 첫 번째 호출에서 저장된 파일 설명자(부모 쉘의 <>&9
설명자에 해당)에 쓰거나 명령을 보내는지 여부에 따라 tty에 씁니다 .
PIP:START
TTY:START
다음과 같은 정보를 보낼 수 있습니다.
printf \\nPIP:START\\n >&8
printf \\nTTY:START\\n >&8
귀하의 필요에 따라 두 가지를 모두 처리합니다. 상위 셸은 trap
명령을 보내도록 구성되어 있습니다.TTY: 중지그것이 수신될 때정수신호 - 그러니 그냥 누르세요Ctrl+C키보드에 있는 내용은 언제든지 터미널에 쓰기를 중지하기에 충분해야 합니다. 그러나 trap
원하는 경우 파이프에 쓰기를 중지하도록 명시적으로 지시해야 합니다 . 다음과 같이 중지하라고 말할 수 있습니다.
printf \\nPIP:STOP\\n >&8
sed
~ 할 것이다언제나 w
표준 출력을 버퍼링하는 방법에 관계없이 라인 버퍼에 씁니다. 이렇게 하면 ping
작성하는 모든 줄을 작성하자마자 읽을 수 있습니다. 파일 설명자 9를 읽기 위해 다른 프로세스를 호출하면 됩니다.그림 속의 그림 sed
지시에 따라 작성하거나 터미널에 직접 인쇄할 수 있습니다. 읽으려면 다른 프로세스를 호출하세요.그림 속의 그림그냥 해:
cat <&9
...또는 유사합니다. 또한 필요한 모든 프로세스가 해당 파이프에 파일 핸들을 명시적으로 설정한 후에는 사용 중인 파이프에 대한 파일 시스템 rm
링크를 명시적으로 생성한다는 점에 유의하세요. mkfifo
이는 파일 시스템을 통해 관련 프로세스와 상호 작용할 수 없음을 의미합니다. 모든 IPC는 상위 셸의 설명자 8 및 9를 통해 조정되어야 합니다. 백그라운드 프로세스 sed
와 ping
프로세스의 PID는 쉘 변수 $PNGPID
와 $SEDPID
. 둘 다 로 해결 가능합니다 kill
.
이제 위의 스크립트 예제를 실행했으므로 다음을 보내십시오.텔레타이프라이터: 시작명령의 결과는 다음과 같습니다.
[mikeserv@localhost ~]$ printf \\nTTY:START\\n >&8
[mikeserv@localhost ~]$ 218 24.2 ms
219 21.2 ms
220 23.1 ms
221 21.3 ms
222 21.9 ms
^C
[mikeserv@localhost ~]$
...하지만 두 프로세스는 여전히 실행 중입니다. sed
어디에도 출력이 기록되지 않습니다.
ps -Fp "$SEDPID" "$PNGPID"
UID PID PPID C SZ RSS PSR STIME TTY STAT TIME CMD
mikeserv 31601 28945 0 2143 1712 4 14:22 pts/0 SN 0:00 sed -n s/^64.*q=\(.*\) ttl.*=/\1\
mikeserv 31602 28945 0 2106 740 3 14:22 pts/0 SN 0:00 ping -On 8.8.8.8
답변3
유닉스는 파이프에 관해서는 정말 제한된 것 같습니다. 의미 있는 작업을 수행하기 전에 먼저 파일에 데이터를 쓴 것 같습니다. 전체 프로세스를 다시 작성하면 가상 터미널이 해결책이 될 수 있습니다. 하지만 멋진 GUI를 사용하는 대신 간단히 터미널을 사용하고 awk를 사용하여 진행률 표시줄을 렌더링했습니다.
ping -i 0.5 8.8.8.8 | gawk -F'[= ]' '/^64/{ for(i=0;i<($(NF-1)/3);i++) printf(" "); printf("|"); for(i=120;i>$(NF-1);i--) printf(" "); printf("\r");}'
120을 터미널 너비로 바꾸세요.