Passando a variável bash para o jq select


112

Eu escrevi um script para recuperar determinado valor file.json. Funciona se eu fornecer o valor para jq select, mas a variável parece não funcionar (ou não sei como usá-la).

#!/bin/sh

#this works ***
projectID=$(cat file.json | jq -r '.resource[] | select(.username=="myemail@hotmail.com") | .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"

Um problema relacionado: passar a variável bash para o filtro jq tem a sintaxe um pouco diferente jq -r --arg var "$var" '.[$var]' stackoverflow.com/questions/34745451/…
enarmônico

Respostas:


176

Considere também passar a variável shell (EMAILID) como uma variável jq (aqui também EMAILID, para fins de ilustração):

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

PostScript

Para o registro, outra possibilidade seria usar a envfunção de jq para acessar variáveis ​​de ambiente. Por exemplo, considere esta sequência de comandos bash:

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

A saída é uma string JSON:

"foo@bar.com"

4
Esta é a única resposta 100% segura; permite jqcriar corretamente o filtro usando o valor, em vez de usar bashpara criar uma string que jqinterpreta como um filtro. (Considere o que acontece se o valor de EMAILIDcontém a ).)
chepner

3
Pico de graças. a solução fornecida é a resposta adequada que eu estava procurando. Sim, funciona.
asidd em

Meu caso de uso, funciona! function n2 { termux-contact-list |jq -r --arg v1 "$1" '.[] | select(.name==$v1)|.number' }Ligando:n2 Name1
Timo

2
fyi, a envfunção de jq é alterada entre jq 1.4 e 1.5+, portanto, referências a env.EMAILID(no exemplo desta resposta) não funcionam em jq 1.4, portanto, recomendamos o uso da --arg EMAILID "$EMAILID"construção se você precisar usar jq 1.4. Espero que isto ajude; levei um dia + para descobrir por mim mesmo;)
m0j0hn

3
Os argumentos passados ​​usando --argserão passados ​​como strings. Se você quiser passar dados de um tipo JSON diferente, use --argjson, que analisará a string como JSON, por exemplo,--argjson myObj '{"a": [1, 2]}'
BallpointBen

25

Resolvi esse problema escapando das aspas duplas internas

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

Estou usando isso porque --arg não joga bem com-c
Dimitris Moraitidis

9

É um problema de cotação, você precisa de:

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

Se você colocar aspas simples para delimitar a string principal, o shell interpretará $EMAILIDliteralmente.

"Quote Double" cada literal que contenha espaços / metacharacters e cada expansão: "$var", "$(command "$var")", "${array[@]}", "a & b". Use 'single quotes'para o código ou literal $'s: 'Costs $5 US', ssh host 'echo "$HOSTNAME"'. Consulte
http://mywiki.wooledge.org/Quotes
http://mywiki.wooledge.org/Arguments
http://wiki.bash-hackers.org/syntax/words


Na minha erro caso também, semelhante, se não o uso de aspas em tudo: termux-contact-list |jq -r '.[] | select(.name=='$1')|.number'. Resultado:jq Compile error
Timo

6
Não sei por que este comentário foi votado tantas vezes; sim, as aspas duplas permitem expansão variável, mas jqnão gosta de aspas simples:jq: error: syntax error, unexpected INVALID_CHARACTER (Unix shell quoting issues?)
jdelman

Se a sua entrada for um json regular, seus valores serão colocados entre aspas duplas (não simples). O que funciona: usar aspas duplas para todo o argumento de jq e escapar (com `\`) de quaisquer aspas duplas, como @asid respondeu.
GuSuku

6

Postando aqui, pois pode ajudar outras pessoas. Na string, pode ser necessário passar as aspas para jq. Para fazer o seguinte com jq:

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

na festa você poderia fazer

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

essencialmente escapando das aspas e passando para jq


Funciona perfeitamente. Não é muito complicado para quem procura uma solução rápida.
Mo-Gang

4

Outra maneira de fazer isso é com o sinalizador jq "--arg". Usando o exemplo original:

#!/bin/sh

#this works ***
projectID=$(cat file.json | jq -r '.resource[] | 
select(.username=="myemail@hotmail.com") | .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"

Veja aqui, onde encontrei esta solução: https://github.com/stedolan/jq/issues/626


2

Eu sei que é um pouco mais tarde para responder, desculpe. Mas isso funciona para mim.

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

E agora posso buscar e passar o conteúdo da variável para 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')"

No meu caso, precisei usar aspas duplas e aspas para acessar o valor da variável.

Felicidades.


1

Jq agora tem uma maneira melhor de acessar as variáveis ​​de ambiente, você pode usar env.EMAILI:

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

0

Pouco não relacionado, mas ainda vou colocá-lo aqui. Para outros fins práticos, as variáveis ​​de shell podem ser usadas como -

value=10
jq  '."key" = "'"$value"'"' file.json
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.