파이프 체인 내에서 jq를 사용하면 출력이 생성되지 않습니다


12

jq출력이 리디렉션 될 때 명시 적 필터 가 필요한 문제 는 웹 전체에서 논의됩니다. 그러나 jq명시 적 필터를 사용하는 경우에도 파이프 체인의 일부인 경우 출력을 리디렉션 할 수 없습니다 .

치다:

touch in.txt
tail -f in.txt | jq '.f1'
# in a different terminal:
echo '{"f1":1,"f2":2}' >> in.txt
echo '{"f1":3,"f2":2}' >> in.txt

예상 한대로 jq명령 의 원래 터미널 출력 은 다음과 같습니다.

1
3

그러나 jq명령 끝에 리디렉션 또는 파이핑을 추가 하면 출력이 자동으로 진행됩니다.

rm in.txt
touch in.txt
tail -f in.txt | jq '.f1' | tee out.txt
# in a different terminal:
echo '{"f1":1,"f2":2}' >> in.txt
echo '{"f1":3,"f2":2}' >> in.txt

첫 번째 터미널에 출력이 나타나지 않고 out.txt가 비어 있습니다.

수백 가지 변형을 시도했지만 찾기 어려운 문제입니다. 은 내가 찾은 해결 을 통해 발견으로, mosquitto_sub그리고 (나는 또한 문제가 발견 된 곳이었다)없는 것들 네트워크, 꼬리 래핑하는 것입니다 쉘 스크립트에서 JQ 기능 :

#!/bin/bash
tail -f $1 | while IFS='' read line; do
echo $line | jq '.f1'
done

그때:

./tail_and_jq.sh | tee out.txt
# in a different terminal:
echo '{"f1":1,"f2":2}' >> in.txt
echo '{"f1":3,"f2":2}' >> in.txt

그리고 충분히 출력이 나타납니다.

1
3

jqHomebrew를 통해 최신 버전으로 설치되어 있습니다.

$ echo $SHELL
/bin/bash
$ jq --version
jq-1.5
$ brew install jq
Warning: jq 1.5_3 is already installed and up-to-date

이것은 jq파이프 체인에 대한 이해와 관련 이 있거나 (문서화되지 않은) 버그 입니까?


1
FWIW tail -f에는 프로그램에 지속적인 입력을 제공 tee하고 출력을 처리하는 데 사용되는 상당히 (잘, 약간) 이상한 설정이 있습니다 . 여전히 대답이 필요한 경우 체인을 단순화하여 <in.json jq '.f1' >out.json원인을 좁힐 수 있습니다.
David Z

참조 BashFAQ # 9 - 버퍼링은 무엇입니까? 또는 왜 내 명령 줄이 출력을 생성하지 tail -f logfile | grep 'foo bar' | awk ...
Charles Duffy

앞으로의 노력에 대한 모든 훌륭한 조언, 감사합니다. FWIW tail에서 파이프를 분리 (첫 번째 명령 실행, 티 및 파일로 리디렉션, 테일, 파이프로 다음 명령으로 이동, 파일로 리디렉션 등)하려는 노력에서 비롯되었습니다. 는 <하지만 염두에 두어야 할 수있는 좋은 도구입니다.
Heath Raftery

답변:


20

로부터의 출력은 jq표준 출력이 파이프 때 버퍼링된다.

jq모든 객체 후에 출력 버퍼 를 플러시하도록 요청하려면 --unbuffered옵션을 사용하십시오.

tail -f in.txt | jq --unbuffered '.f1' | tee out.txt

로부터 jq수동 :

--unbuffered

각 JSON 객체가 인쇄 된 후 출력을 플러시합니다 (느린 데이터 소스를 jq파이핑하고 jq다른 곳의 출력을 파이핑하는 경우 유용 ).


또한 출력 버퍼링이 문제임을 알기 위해 이것을 디버깅하는 방법은 단순히 추측하지 않을 것이라고 가정하면 'ltrace'및 / 또는 'strace'에서 'jq'부분을 실행하는 것입니다. C stdio 출력 함수를 호출하지만 write (2) syscall을 호출하지 않는 것이 분명합니다.
AnotherSmellyGeek

1
@AnotherSmellyGeek 가능하거나 Unices의 동등한 추적 유틸리티 (OP는 Homebrew를 사용하고 있습니다. 즉, MacOS를 사용하고 있으며 OpenBSD를 사용하고 있으며 Linux 도구는 없습니다). 또 다른 가능성은 출력 버퍼링이 특정 상황에서 발생할 수 있음을 아는 것입니다. :-)
Kusalananda

훌륭한. 앞으로 디버깅에 대한 모든 조언을 진심으로 감사드립니다. 버퍼링은 내가 처음 의심 한 것 중 하나 였지만 파이핑의 다른 동작은 디버깅 작업을 플럭 싱하는 것이 었습니다.
Heath Raftery

6

여기서보고있는 것은 C stdio 버퍼링 작동입니다. 특정 한계 (512 바이트 또는 4KB 이상)에 도달 할 때까지 출력을 버퍼에 저장 한 다음 한 번에 전송합니다.

이 버퍼링은 stdout이 터미널에 연결된 경우 자동으로 비활성화되지만 파이프와 같은 경우 (예 : 경우와 같이)이 버퍼링 동작을 활성화합니다.

버퍼링을 비활성화 / 제어하는 ​​일반적인 방법은 setvbuf()기능을 사용하는 것입니다 ( 자세한 내용 은 이 답변 참조). 소스 코드 jq자체 에서 수행해야 하므로 실제적이지 않을 수도 있습니다 ...

해결 방법이 있습니다 ... (해킹, 아마도 말할 수 있습니다.) "언 버퍼"라는 프로그램이 있습니다. "버퍼"는 의사 터미널을 만들고 프로그램에 연결할 수있는 "expect"로 배포됩니다. 따라서 jq파이프에 계속 쓰 더라도 터미널에 쓰고 있다고 생각하고 버퍼링 효과가 비활성화됩니다.

"expect"패키지를 설치하십시오. 만약 "unbuffer"가 없다면, 데비안 (또는 우분투)에 설치하십시오 :

$ sudo apt-get install expect

그런 다음이 명령을 사용할 수 있습니다.

$ tail -f in.txt | unbuffer -p jq '.f1' | tee out.txt

"언 버퍼"에 대한 자세한 내용은 이 답변참조하십시오 . 맨 페이지도 여기에서 찾을 수 있습니다 .


관찰 된 동작이 발생하는 이유를 설명했지만 Kusalananda가 지적했듯이 jq기본적으로 버퍼링되지 않은 출력을 구현하므로 해결 방법이 필요하지 않습니다.
David Z

아 아주 좋아! 나는 jq맨 페이지를 찾기 시작 했지만 잠시 후에 지루해하고 다른 일을하러 갔다. :-)
filbranden

1
Protip, GNU coreutils에는 stdbuf -o0LD_PRELOAD를 통해 코드를 삽입하고 setvbuf()마법을 부를 것입니다. 그것이 macOS에서 작동하는지 여부는 확실하지 않습니다.
user1686

1
expectmacos에 사전 설치되어 있지만 unbuffer그렇지 않습니다. 그러나 홈 브루 패키지의 일부이므로 macos에서도 마찬가지입니다 brew install expect.
히스 서까래
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.