Przekazywanie zmiennej bash do jq

113

Napisałem skrypt do pobierania określonej wartości file.json. Działa, jeśli podam wartość do jq select, ale zmienna wydaje się nie działać (lub nie wiem, jak jej użyć).

#!/bin/sh

#this works ***
projectID=$(cat file.json | jq -r '.resource[] | select(.username=="[email protected]") | .id')
echo "$projectID"

EMAILID=myemail@hotmail.com

#this does not work *** no value is printed
projectID=$(cat file.json | jq -r '.resource[] | select(.username=="$EMAILID") | .id')
echo "$projectID"
asidd
źródło
Powiązany problem: przekazywanie zmiennej bash do filtra jq ma nieco inną składnię jq -r --arg var "$var" '.[$var]' stackoverflow.com/questions/34745451/ ...
enharmonic

Odpowiedzi:

177

Rozważ również przekazanie zmiennej powłoki (EMAILID) jako zmiennej jq (tutaj również EMAILID, dla ilustracji):

   projectID=$(cat file.json | 
     jq -r --arg EMAILID "$EMAILID" '
        .resource[]
        | select(.username==$EMAILID) 
        | .id')

Postscriptum

Dla przypomnienia, inną możliwością byłoby użycie envfunkcji jq do uzyskiwania dostępu do zmiennych środowiskowych. Weźmy na przykład pod uwagę następującą sekwencję poleceń bash:

EMAILID=foo@bar.com  # not exported
EMAILID="$EMAILID" jq -n 'env.EMAILID'

Dane wyjściowe to ciąg JSON:

"[email protected]"
szczyt
źródło
4
To jedyna w 100% bezpieczna odpowiedź; pozwala jqpoprawnie utworzyć filtr przy użyciu wartości, zamiast używać go bashdo tworzenia łańcucha, który jest jqinterpretowany jako filtr. (Zastanów się, co się stanie, jeśli wartość EMAILIDzawiera a ).)
chepner
3
Dzięki, szczyt. Twoje dostarczone rozwiązanie jest właściwą odpowiedzią, której szukałem. Tak to działa.
asidd
Mój przypadek użycia, działa! function n2 { termux-contact-list |jq -r --arg v1 "$1" '.[] | select(.name==$v1)|.number' }Dzwonię:n2 Name1
Timo
2
fyi, envfunkcja jq jest zmieniana między jq 1.4 a 1.5+, więc odniesienia do env.EMAILID(w przykładzie tej odpowiedzi) nie działają w jq 1.4, dlatego zaleca się użycie --arg EMAILID "$EMAILID"konstrukcji, jeśli musisz użyć jq 1.4. Mam nadzieję że to pomoże; zajęło mi tylko dzień +, żeby samemu to
rozgryźć
3
Argumenty przekazane za pomocą --argzostaną przekazane jako ciągi. Jeśli chcesz przekazać dane innego typu JSON, użyj --argjson, który przeanalizuje ciąg jako JSON, np.--argjson myObj '{"a": [1, 2]}'
BallpointBen
25

Rozwiązałem ten problem, unikając wewnętrznych podwójnych cudzysłowów

projectID=$(cat file.json | jq -r ".resource[] | select(.username==\"$EMAILID\") | .id")
asidd
źródło
Używam tego, ponieważ --arg nie gra miło z-c
Dimitris Moraitidis
9

To kwestia wyceny, potrzebujesz:

projectID=$(
  cat file.json | jq -r ".resource[] | select(.username=='$EMAILID') | .id"
)

Jeśli użyjesz pojedynczych cudzysłowów, aby oddzielić główny ciąg, powłoka przyjmuje $EMAILIDdosłownie.

„Double cytat” każdy dosłowne, że zawiera spacje / metaznaki i każdy rozbudowa: "$var", "$(command "$var")", "${array[@]}", "a & b". Użyj 'single quotes'kodu lub dosłowny $'s: 'Costs $5 US', ssh host 'echo "$HOSTNAME"'. Zobacz
http://mywiki.wooledge.org/Quotes
http://mywiki.wooledge.org/Arguments
http://wiki.bash-hackers.org/syntax/words

Gilles Quenot
źródło
W moim przypadku błędu, jak również, podobnie, jeśli nie w ogóle za pomocą cytatów: termux-contact-list |jq -r '.[] | select(.name=='$1')|.number'. Wynik:jq Compile error
Timo
6
Nie jestem pewien, dlaczego ten komentarz był głosowany tyle razy; tak, podwójne cudzysłowy pozwalają na rozwinięcie zmiennych, ale jqnie lubią pojedynczych cudzysłowów:jq: error: syntax error, unexpected INVALID_CHARACTER (Unix shell quoting issues?)
jdelman
Jeśli dane wejściowe to zwykły plik json, jego wartości będą podwójnie (nie pojedynczo) cytowane. Co działa: używanie podwójnych cudzysłowów dla całego argumentu jq i unikanie (z `\`) wszelkich podwójnych cudzysłowów w obrębie, jak odpowiedział @asid.
GuSuku
6

Publikując go tutaj, ponieważ może to pomóc innym. W łańcuchu może być konieczne przekazanie cudzysłowów do jq. Aby wykonać następujące czynności z jq:

.items[] | select(.name=="string")

w bash możesz zrobić

EMAILID=$1
projectID=$(cat file.json | jq -r '.resource[] | select(.username=='\"$EMAILID\"') | .id')

zasadniczo unikając cudzysłowów i przekazując je do jq

Zaid
źródło
Działa świetnie. Niezbyt skomplikowane dla kogoś, kto szuka szybkiego rozwiązania.
Mo-Gang
4

Innym sposobem osiągnięcia tego jest użycie flagi jq "--arg". Korzystając z oryginalnego przykładu:

#!/bin/sh

#this works ***
projectID=$(cat file.json | jq -r '.resource[] | 
select(.username=="[email protected]") | .id')
echo "$projectID"

EMAILID=myemail@hotmail.com

# Use --arg to pass the variable to jq. This should work:
projectID=$(cat file.json | jq --arg EMAILID $EMAILID -r '.resource[] 
| select(.username=="$EMAILID") | .id')
echo "$projectID"

Zobacz tutaj, gdzie znalazłem to rozwiązanie: https://github.com/stedolan/jq/issues/626

Andrew Lockhart
źródło
2

Wiem, że odpowie trochę później, przepraszam. Ale to działa dla mnie.

export K8S_public_load_balancer_url="$(kubectl get services -n ${TENANT}-production -o wide | grep "ingress-nginx-internal$" | awk '{print $4}')"

Teraz jestem w stanie pobrać i przekazać zawartość zmiennej do jq

export TF_VAR_public_load_balancer_url="$(aws elbv2 describe-load-balancers --region eu-west-1 | jq -r '.LoadBalancers[] | select (.DNSName == "'$K8S_public_load_balancer_url'") | .LoadBalancerArn')"

W moim przypadku potrzebowałem użyć podwójnego cudzysłowu i cudzysłowu, aby uzyskać dostęp do wartości zmiennej.

Twoje zdrowie.

Rodrigo Andrade
źródło
1

Jq ma teraz lepszy sposób na dostęp do zmiennych środowiskowych, możesz użyć env.EMAILI:

projectID=$(cat file.json | jq -r ".resource[] | select(.username==env.EMAILID) | .id")
DukeLion
źródło
0

Trochę niezwiązane, ale nadal będę to umieszczać tutaj, Do innych praktycznych celów zmienne powłoki mogą być używane jako -

value=10
jq  '."key" = "'"$value"'"' file.json
markroxor
źródło