상당히 복잡한 두 개의 표현식이 있지만 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배 느림) .