선택 항목이나 해당 보완 항목을 반환하기 위해 "jq" 표현식을 매개변수화하는 방법은 무엇입니까?

선택 항목이나 해당 보완 항목을 반환하기 위해 "jq" 표현식을 매개변수화하는 방법은 무엇입니까?

상당히 복잡한 두 개의 표현식이 있지만 jq하나가 다른 하나의 보수를 반환한다는 점만 다릅니다. 즉, 둘 사이의 유일한 차이점은 하나가 select(expression)다른 하나가 반환하는 것을 반환한다는 것입니다 select(expression|not).

단순화된 예:

$ jq -n '$ARGS.positional[] | select( . > 2 )' --jsonargs 1 2 3 4 5
3
4
5
$ jq -n '$ARGS.positional[] | select( . > 2 | not )' --jsonargs 1 2 3 4 5
1
2

내 코드에서 이 두 가지 다른 표현식을 반복하는 대신 jq(각 표현식은 실제로 몇 줄 길이입니다) 단일 표현식에 값을 전달하여 두 동작 사이를 전환할 수 있습니다. 이는 매우 간결합니다.

어떻게 해야 하나요?


실제 jq코드(메시지 페이로드에 인코딩된 파일 경로를 기반으로 RabbitMQ 메시지 필터링):

map(
        # Add an array of pathnames that would match this message.  This
        # includes the pathnames of each parent directory, leading up to
        # and including the pathname of the file itself.
        .tmp_paths = [
                # The full pathname is part of a base64-encodod JSON blob.
                foreach (
                        .payload |
                        @base64d |
                        fromjson.filepath |
                        split("/")[]
                ) as $elem (
                        null;
                        . += $elem + "/";
                        .
                )

        ] |
        # The last element is the full file path and should not have a
        # trailing slash.
        .tmp_paths[-1] |= rtrimstr("/")
) |
[
        # Match the pathnames given as positional command line arguments
        # against the computed pathnames in the "tmp_paths" array in
        # each message.  Extract the messages with a match.
        JOIN(
                INDEX($ARGS.positional[]; .);
                .[];
                .tmp_paths[];
                if (.[1:] | any) then
                        .[0]
                else
                        empty
                end
        )
] |
# Deduplicate the extracted messages on the full pathname of the file.
# Then remove the "tmp_paths" array from each message and base64 encode
# them.
unique_by(.tmp_paths[-1])[] |
del(.tmp_paths) |
@base64

if나는 파일 경로가 위치 인수로 제공된 경로 이름과 일치하는 메시지를 추출하거나 삭제하도록 어떤 방식으로든 명령문을 수정 해야 한다고 가정합니다 .

답변1

부울 값을 jq표현식에 전달하고 if- 문을 사용하여 선택한 세트 또는 해당 보수 반환 간에 전환합니다.

$ jq -n --argjson yes true '$ARGS.positional[] | select( . > 2 | if $yes then . else not end )' --jsonargs 1 2 3 4 5
3
4
5
$ jq -n --argjson yes false '$ARGS.positional[] | select( . > 2 | if $yes then . else not end )' --jsonargs 1 2 3 4 5
1
2

더 복잡한 jq표현식 에서는 if명령문을 수정합니다.

# Match the pathnames given as positional command line arguments
# against the computed pathnames in the "tmp_paths" array in
# each message.  Depending on the $yes boolean variable, extract
# or discard matching messages.
JOIN(
        INDEX($ARGS.positional[]; .);
        .[];
        .tmp_paths[];
        if (.[1:] | any | if $yes then . else not end) then
                .[0]
        else
                empty
        end
)

if $yes then . else not end이는 변수가 $yes집합을 원하는지 아니면 그 보수를 원하는지 여부에 대한 "토글" 역할을 할 수 있다는 점에 유의하세요 . 단순화된 select()형식과 보다 복잡한 형식 모두에서 JOIN()if명령문은 부울 테스트 결과에 대해 작동하여 요소가 결과 세트의 일부인지 여부를 결정합니다.

답변2

이것@Kusalananda가 설명하는 솔루션간단하고 읽기 쉽고 컴팩트하며 매우 빠르기 때문에 모든 일반적인 상황, 특히 가끔씩 발생하는 경우를 처리하는 가장 좋은 방법일 것입니다.

안정적인 설정을 보장하기 위해 이 전환 동작을 자주 사용하거나 속도를 높이기 위해 추가 노력을 기울이려는 경우 다른 접근 방식을 고려할 수 있습니다.

실제로 이 훌륭하고 간단한 접근 방식은 if ... then ... else ... end스트림의 각 개체에 대해 추가 비교를 추가해야 한다는 단점이 있습니다. 이 비교는 결과가 항상 명령줄의 정적 입력으로 미리 알려지고 실행 중에 변경되지 않기 때문에 약간 낭비가 됩니다.

이러한 비교를 제거하는 한 가지 가능한 방법은 모듈에 정의된 함수를 사용한 다음 명령줄에서 해당 함수를 선택하는 것입니다.

고려하다:

# let's set the thing up
$ mkdir dot && echo 'def dot_or_not: .;' > dot/.jq
$ mkdir not && echo 'def dot_or_not: not;' > not/.jq
# now let's use it
$ seq 5 | jq 'include "./"; select ( . > 2 | dot_or_not )' -Ldot
3
4
5
$ seq 5 | jq 'include "./"; select ( . > 2 | dot_or_not )' -Lnot
1
2

단일 프로세서 가상 머신에 대한 일부 간단한 벤치마크에서 이 접근 방식은 기본 접근 방식보다 평균 5배 더 빠르지만 if ... then ... else ... end시연한 대규모 계산의 "경제성"에는 영향을 미치지 않을 것입니다.

나 자신도 아마도 그렇게 간단한 작업을 위해 여기까지 가지 않을 것입니다... 다른 가능한 (더 가치 있는) 모듈 위에 각각의 추가 "스위치"가 점점 더 번거롭고 다루기 어려워지기 때문입니다. 사실 저는 정말 다양한 계산 변형을 위해 모듈을 사용하고 싶지만... 그래도 그렇습니다.


완전성을 기하기 위해 스펙트럼의 반대쪽 끝에서는 또 다른 접근 방식이 다음과 같이 보일 수 있습니다.

$ seq 5 | jq 'select( . > 2 | [not,.][$yes] )' --argjson yes 1
3
4
5
$ seq 5 | jq 'select( . > 2 | [not,.][$yes] )' --argjson yes 0
1
2

또는 그 사촌 변형:

$ seq 5 | jq 'select( . > 2 | {(tostring):1}[$yes] )' --arg yes true 
3
4
5
$ seq 5 | jq 'select( . > 2 | {(tostring):1}[$yes] )' --arg yes false 
1
2

if ... then ... else ... end이러한 방법은 컴팩트해 보이지만 불행하게도 각각 2개의 휘발성 개체 구성과 조회를 추가하기 때문에 기본 방법보다 훨씬 느립니다(간단한 벤치마크에서는 2~4배 느림) .

관련 정보