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

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

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_subIoT를 통해 발견한 대로(여기서 문제를 발견했습니다) 꼬리를 감싸는 것입니다.그리고쉘 스크립트의 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

jq표준 출력이 터미널이 아닌 경우 해당 출력은 버퍼링됩니다.

jq각 객체의 출력 버퍼가 이후에 플러시되도록 요청하려면 --unbuffered해당 옵션을 사용하세요.

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

jq매뉴얼 에서 :

--unbuffered

각 JSON 개체를 인쇄한 후 출력을 플러시합니다( 느린 데이터 소스를 다른 곳으로 파이프 jq하고 출력을 파이프 하는 경우 jq유용함 ).

답변2

여기서 볼 수 있는 것은 C stdio 버퍼링이 작동하는 모습입니다. 특정 제한(512바이트, 4KB 이상)에 도달할 때까지 출력을 버퍼에 저장한 다음 모든 출력을 한 번에 보냅니다.

stdout이 터미널에 연결되면 이 버퍼링은 자동으로 비활성화되지만 파이프에 연결되면(귀하의 경우처럼) 이 버퍼링 동작이 활성화됩니다.

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

해결 방법이 있습니다... (누군가는 해킹이라고 말할 수도 있습니다.) "expect"와 함께 배포되는 "unbuffer"라는 프로그램이 있는데, 이 프로그램은 의사 터미널을 생성하고 이를 프로그램에 연결합니다. 따라서 jq파이프에 계속 쓰고 있더라도 터미널에 쓰고 있다고 생각하여 버퍼링 효과가 비활성화됩니다.

아직 없는 경우 "unbuffer"와 함께 제공되는 "expect" 패키지를 설치합니다. 예를 들어 Debian(또는 Ubuntu)의 경우:

$ sudo apt-get install expect

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

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

당신은 또한 볼 수 있습니다이 답변"unbuffer"에 대한 자세한 내용을 찾을 수 있습니다.매뉴얼 페이지도 여기에 있습니다.

관련 정보