AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 413878
Accepted
EHerman
EHerman
Asked: 2017-12-31 14:50:58 +0800 CST2017-12-31 14:50:58 +0800 CST 2017-12-31 14:50:58 +0800 CST

Matriz JSON para bash variáveis ​​usando jq

  • 772

Eu tenho uma matriz JSON assim:

{
  "SITE_DATA": {
    "URL": "example.com",
    "AUTHOR": "John Doe",
    "CREATED": "10/22/2017"
  }
}

Estou procurando iterar sobre esta matriz usando jq para poder definir a chave de cada item como o nome da variável e o valor como seu valor.

Exemplo:

  • URL="exemplo.com"
  • AUTOR="John Doe"
  • CRIADO="22/10/2017"

O que eu tenho até agora itera sobre a matriz, mas cria uma string:

constants=$(cat ${1} | jq '.SITE_DATA' | jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]")

Quais saídas:

URL=example.com
AUTHOR=John Doe
CREATED=10/22/2017

Estou procurando usar essas variáveis ​​mais abaixo no script:

echo ${URL}

Mas isso ecoa uma saída vazia no momento. Acho que preciso de um evalou algo lá, mas não consigo identificar.

bash jq
  • 4 4 respostas
  • 58761 Views

4 respostas

  • Voted
  1. Best Answer
    Michael Homer
    2017-12-31T15:10:04+08:002017-12-31T15:10:04+08:00

    Sua versão original não será evalcapaz porque o nome do autor contém espaços - seria interpretado como a execução de um comando Doecom a variável de ambiente AUTHORdefinida como John. Também praticamente nunca há necessidade de canalizar jqpara si mesmo - a tubulação interna e o fluxo de dados podem conectar diferentes filtros.

    Tudo isso só faz sentido se você confiar totalmente nos dados de entrada (por exemplo, gerados por uma ferramenta que você controla). Existem vários problemas possíveis detalhados abaixo, mas vamos supor que os dados em si certamente estejam no formato que você espera no momento.

    Você pode fazer uma versão muito mais simples do seu programa jq:

    jq -r '.SITE_DATA | to_entries | .[] | .key + "=" + (.value | @sh)'
    

    quais saídas:

    URL='example.com'
    AUTHOR='John Doe'
    CREATED='10/22/2017'
    

    Não há necessidade de um map: .[]trata de levar cada objeto no array pelo resto do pipeline como um item separado , então tudo depois do último |é aplicado a cada um separadamente. No final, apenas montamos uma string de atribuição de shell válida com +concatenação comum, incluindo aspas apropriadas e escapando em torno do valor com@sh .

    Todos os canais são importantes aqui - sem eles, você recebe mensagens de erro bastante inúteis, onde partes do programa são avaliadas em contextos sutilmente diferentes.

    Essa string é evalpossível se você confiar totalmente nos dados de entrada e tiver o efeito desejado:

    eval "$(jq -r '.SITE_DATA | to_entries | .[] | .key + "=" + (.value | @sh)' < data.json)"
    echo "$AUTHOR"
    

    Como sempre, ao usar evalo , tenha cuidado para confiar nos dados que está obtendo, pois se for malicioso ou apenas em um formato inesperado, as coisas podem dar muito errado. Em particular, se a chave contiver metacaracteres de shell como $ou espaço em branco, isso poderá criar um comando em execução. Ele também pode sobrescrever, por exemplo, a PATHvariável de ambiente inesperadamente.

    Se você não confia nos dados, não faça isso ou filtre o objeto para conter apenas as chaves que você deseja primeiro:

    jq '.SITE_DATA | { AUTHOR, URL, CREATED } | ...'
    

    Você também pode ter um problema no caso de o valor ser uma matriz, então .value | tostring | @shserá melhor - mas esta lista de ressalvas pode ser um bom motivo para não fazer nada disso em primeiro lugar.


    Também é possível construir uma matriz associativa onde as chaves e os valores são citados:

    eval "declare -A data=($(jq -r '.SITE_DATA | to_entries | .[] | @sh "[\(.key)]=\(.value)"' < test.json))"
    

    Depois disso, ${data[CREATED]}contém a data de criação e assim por diante, independentemente de qual seja o conteúdo das chaves ou valores. Essa é a opção mais segura, mas não resulta em variáveis ​​de nível superior que possam ser exportadas. Ainda pode produzir um erro de sintaxe Bash quando um valor é uma matriz ou um erro jq se for um objeto, mas não executará o código nem substituirá nada.

    • 45
  2. cas
    2017-12-31T18:36:59+08:002017-12-31T18:36:59+08:00

    Com base na resposta de @Michael Homer, você pode evitar evaltotalmente um potencial inseguro lendo os dados em uma matriz associativa.

    Por exemplo, se seus dados JSON estiverem em um arquivo chamado file.json:

    #!/bin/bash
    
    typeset -A myarray
    
    while IFS== read -r key value; do
        myarray["$key"]="$value"
    done < <(jq -r '.SITE_DATA | to_entries | .[] | .key + "=" + .value ' file.json)
    
    # show the array definition
    typeset -p myarray
    
    # make use of the array variables
    echo "URL = '${myarray[URL]}'"
    echo "CREATED = '${myarray[CREATED]}'"
    echo "AUTHOR = '${myarray[URL]}'"
    

    Resultado:

    $ ./read-into-array.sh 
    declare -A myarray=([CREATED]="10/22/2017" [AUTHOR]="John Doe" [URL]="example.com" )
    URL = 'example.com'
    CREATED = '10/22/2017'
    AUTHOR = 'example.com'
    
    • 19
  3. EHerman
    2017-12-31T14:54:28+08:002017-12-31T14:54:28+08:00

    Acabei de perceber que posso percorrer os resultados e avaliar cada iteração:

    constants=$(cat ${1} | jq '.SITE_DATA' | jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]")
    
    for key in ${constants}; do
      eval ${key}
    done
    

    Permite-me fazer:

    echo ${AUTHOR}
    # outputs John Doe
    
    • 3
  4. Thiago Conrado
    2019-08-28T12:01:47+08:002019-08-28T12:01:47+08:00

    Gostei muito da sugestão do @Michel. Às vezes, você pode apenas extrair o valor de algumas variáveis ​​para executar uma tarefa naquele servidor específico usando o Bash. Então, se as variáveis ​​desejadas são conhecidas, usar essa abordagem é a forma de evitar várias chamadas para jqdefinir um valor por variável ou mesmo usar a readinstrução com várias variáveis ​​em que algumas podem ser válidas e vazias, levando a uma mudança de valor (isso foi meu problema).

    Minha abordagem anterior levaria a um erro de deslocamento de valor – se .svID[ ].ID="", svobterá o valor slotID .

    -rd '\n' getInfo sv slotID <<< $(jq -r '(.infoCMD // "no info"), (.svID[].ID // "none"), (._id // "eeeeee")' <<< $data)
    

    Se você baixou o objeto usando curl, aqui está minha abordagem para renomear algumas variáveis ​​para um nome amigável como extrair dados de matrizes de dados.

    Usar eval e filtros resolverá o problema com uma linha e produzirá variáveis ​​com os nomes desejados.

    eval "$(jq -r '.[0] | {varNameExactasJson, renamedVar1: .var1toBeRenamed, varfromArray: .array[0].var, varValueFilter: (.array[0].textVar|ascii_downcase)} | to_entries | .[] | .key + "=\"" + (.value | tostring) + "\""' <<< /path/to/file/with/object )"  
    

    A vantagem neste caso, é o fato de que ele irá filtrar, renomear, formatar todas as variáveis ​​desejadas na primeira etapa. Observe que há .[0] | é muito comum ter se a fonte for de um servidor RESTful API usando GET, dados de resposta como:

    [{"varNameExactasJson":"this value", "var1toBeRenamed: 1500, ....}]
    

    Se seus dados não são de um array, ou seja, é um objeto como:

    {"varNameExactasJson":"this value", "var1toBeRenamed: 1500, ....}
    

    apenas remova o índice inicial:

    eval "$(jq -r '{varNameExactasJson, renamedVar1: .var1toBeRenamed, varfromArray: .array[0].var, varValueFilter: (.array[0].textVar|ascii_downcase)} | to_entries | .[] | .key + "=\"" + (.value | tostring) + "\""' <<< /path/to/file/with/object )"
    
    • 0

relate perguntas

  • jq adicionar ou atualizar um valor com vários --arg

  • Problema estranho ao passar variáveis ​​do arquivo de texto

  • Enquanto a linha lê mantendo os espaços de escape?

  • ordem de substituição de processos `te` e `bash`

  • Execute um script muito lento até que seja bem-sucedido

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Matriz JSON para bash variáveis ​​usando jq

    • 4 respostas
  • Marko Smith

    A data pode formatar a hora atual para o fuso horário GMT? [duplicado]

    • 2 respostas
  • Marko Smith

    bash + lê variáveis ​​e valores do arquivo pelo script bash

    • 4 respostas
  • Marko Smith

    Como posso copiar um diretório e renomeá-lo no mesmo comando?

    • 4 respostas
  • Marko Smith

    conexão ssh. Conexão X11 rejeitada devido a autenticação incorreta

    • 3 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Marko Smith

    comando systemctl não funciona no RHEL 6

    • 3 respostas
  • Marko Smith

    rsync porta 22 e 873 uso

    • 2 respostas
  • Marko Smith

    snap /dev/loop em 100% de utilização -- sem espaço livre

    • 1 respostas
  • Marko Smith

    chave de impressão jq e valor para todos no subobjeto

    • 2 respostas
  • Martin Hope
    EHerman Matriz JSON para bash variáveis ​​usando jq 2017-12-31 14:50:58 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST
  • Martin Hope
    Drux A data pode formatar a hora atual para o fuso horário GMT? [duplicado] 2017-12-26 11:35:07 +0800 CST
  • Martin Hope
    AllisonC Como posso copiar um diretório e renomeá-lo no mesmo comando? 2017-12-22 05:28:06 +0800 CST
  • Martin Hope
    Steve Como as permissões de arquivo funcionam para o usuário "root"? 2017-12-22 02:46:01 +0800 CST
  • Martin Hope
    Bagas Sanjaya Por que o Linux usa LF como caractere de nova linha? 2017-12-20 05:48:21 +0800 CST
  • Martin Hope
    Cbhihe Altere o editor padrão para vim para _ sudo systemctl edit [unit-file] _ 2017-12-03 10:11:38 +0800 CST
  • Martin Hope
    showkey Como baixar o pacote não instalá-lo com o comando apt-get? 2017-12-03 02:15:02 +0800 CST
  • Martin Hope
    youxiao Por que os diretórios /home, /usr, /var, etc. têm o mesmo número de inode (2)? 2017-12-02 05:33:41 +0800 CST
  • Martin Hope
    user223600 gpg — o comando list-keys gera uid [ desconhecido ] depois de importar a chave privada para uma instalação limpa 2017-11-26 18:26:02 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve