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 / 776914
Accepted
Marc Le Bihan
Marc Le Bihan
Asked: 2024-05-22 13:37:27 +0800 CST2024-05-22 13:37:27 +0800 CST 2024-05-22 13:37:27 +0800 CST

Tenho uma maneira mais limpa de atribuir uma string json parametrizada a uma variável bash?

  • 772

Gostaria de preparar um teste para um servidor REST petstore por meio de curl.

Para testar sua add new petAPI, preparo uma string json que contém o Petpara inserir em uma função bash que executa curl:

#!/bin/bash

# Add new pet
add_petstore() {
  local rest_url=$1

  local id_pet="$2"
  local id_category="$3"
  local category_name="$4"
  local pet_name="$5"
  local photo_url="$6"
  local tag_id="$7"
  local tag_name="$8"

  local pet_to_add="{ \
        \"id\": $id_pet, \
        \"category\": {  \
          \"id\": $id_category,  \
          \"name\": \"$category_name\"  \
        },  \
        \"name\": \"$pet_name\",  \
        \"photoUrls\": [  \
          \"$photo_url\"  \
        ],  \
        \"tags\": [  \
          {
            \"id\": $tag_id,  \
            \"name\": \"$tag_name\"  \
          }  \
        ]  \
      }"

  echo "$pet_to_add"

  curl -X 'POST' \
    "$rest_url" \
    -H 'accept: application/xml' \
    -H 'Content-Type: application/json' \
    -d "$pet_to_add"
}

add_petstore "http://localhost:8080/pet" "151" "12" "Dogs" "REX" "http://photosofrex/rex_rage.jpg" "1" "ferocious sales"

O echode $pet_to_addparece o que eu estou disposto a ter:

{         "id": 151,         "category": {            "id": 12,            "name": "Dogs"          },          "name": "REX",          "photoUrls": [            "http://photosofrex/rex_rage.jpg"          ],          "tags": [            {
            "id": 1,              "name": "ferocious sales"            }          ]        }

e add_petstoreo método me permite preparar facilmente alguns animais de estimação.


Mas a local pet_to_add=...declaração é muito suja.
Se alguém (ou eu mais tarde) precisar adaptar esse script, essa variável local não será acolhedora.

Pensei primeiro que poderia colocar seu conteúdo em um arquivo e lê-lo com uma extensão local pet_to_add=$(cat myfile). Mas isso não resolveria seus parâmetros variáveis.

Tenho uma maneira de escrever essa local pet_to_adddeclaração de maneira mais limpa?

bash
  • 3 3 respostas
  • 809 Views

3 respostas

  • Voted
  1. Best Answer
    Sotto Voce
    2024-05-22T14:12:36+08:002024-05-22T14:12:36+08:00

    Sim, existe uma maneira mais limpa para muitos tipos de documentos JSON (embora não para todos os tipos), um documento aqui:

    local pet_to_add=$(cat - <<END_DOC
      {
        "id": $id_pet,
        "category": {
          "id": $id_category,
          "name": "$category_name"
        },
        "name": "$pet_name",
        "photoUrls": [
          "$photo_url"
        ],
        "tags": [
          {
            "id": $tag_id,
            "name": "$tag_name"
          }
        ]
      }
    END_DOC
    )
    

    Para esse tipo de caso de uso, penso em um documento aqui como uma forma de string entre aspas duplas porque expande variáveis ​​​​de shell/ambiente no documento, mas não remove caracteres de aspas duplas porque eles não são os delimitadores de string.


    Quando o seu documento JSON é pequeno, você pode usar o printfcomando interno para inserir valores no meio do documento e salvar o JSON resultante em uma variável shell:

    local example_json
    printf -v example_json '{ "name": "%s", "id": "%s" }' "${pet_name}" "${id_pet}"
    
    • 12
  2. Kusalananda
    2024-05-22T16:18:36+08:002024-05-22T16:18:36+08:00

    Usando jqpara criar partes de JSON para cada subelemento do documento de carga útil de nível superior e, em seguida, juntar tudo. Dessa forma, você garante que jqterá a oportunidade de codificar cada string e evita injetar variáveis ​​de shell no JSON (o que pode quebrar o documento; veja, por exemplo, o $pet_namevalor usado abaixo).

    Primeiro, você configuraria as variáveis ​​do shell com seus valores. Você pode usar valores lidos na linha de comando.

    pet_id=bee001
    pet_name='Eric "the half a bee" II'
    
    category_id=bee
    category_name='The bees'
    
    # I elected to do the tags as an associative array.
    declare -A tags
    tags=(
            [beauty]=high
            [cost]=medium
            [social]=low
    )
    
    # I'm assuming there can by many photo URLs.
    photo_URLs=(
            "url1"
            "url2"
            "url3"
    )
    

    Em seguida, criamos o JSON para a categoryparte:

    # Create category
    category_json=$(
            jq -c -n \
                    --arg id "$category_id" \
                    --arg name "$category_name" \
                    '$ARGS.named'
    )
    

    Isso criaria um único objeto JSON com chaves ide name. Os valores da chave são obtidos das variáveis ​​​​do shell fornecidas. A $ARGS.namedcoisa é um objeto interno jqcontendo todas as chaves + valores fornecidos na linha de comando usando --argou --argjson(para valores que não deveriam ser, ou já são, codificados em string). Como a jqexpressão está entre aspas simples, o shell não será confundido $ARGScom uma variável de shell.

    Em seguida, criamos o array de URL da foto para a photoUrlspeça:

    # Create photo URL array
    photoUrls_json=$(
            jq -c -n \
                    '$ARGS.positional' \
                    --args "${photo_URLs[@]}"
    )
    

    Isso usa jqwith --args, que preenche a $ARGS.positionalmatriz interna com os valores fornecidos. Observe que --argse sua lista de argumentos deve sempre ocorrer por último na linha de comando.

    A seguir, criamos a tagsparte do nosso array associativo:

    # Create tags
    tags_json=$(
            for tag_id in "${!tags[@]}"; do
                    jq -c -n \
                            --arg id "$tag_id" \
                            --arg name "${tags[$tag_id]}" \
                            '$ARGS.named'
            done |
            jq -c -s '.'
    )
    

    Criamos os elementos do array a partir das chaves e valores do array shell associativo em um loop e os inserimos em um array com jq -sleitura do loop.

    Se os IDs das suas tags forem numéricos, faça tagscomo uma bashmatriz comum:

    tags=(                                                                                                                                                                    
            [1]=high                                                                                                                                                          
            [12]=medium                                                                                                                                                       
            [90]=low                                                                                                                                                          
    )
    

    ... e depois converta-o em JSON usando

    # Create tags
    tags_json=$(
            for tag_id in "${!tags[@]}"; do
                    jq -c -n \
                            --argjson id "$tag_id" \
                            --arg name "${tags[$tag_id]}" \
                            '$ARGS.named'
            done |
            jq -c -s '.'
    )
    

    Observe --argjson id "$tag_id"no lugar de --arg id "$tag_id"passar o número, que não deve ser convertido em string.

    Podemos então finalmente reunir a carga JSON destas partes:

    # Create final JSON payload
    payload_json=$(
            jq -c -n \
                    --arg id "$pet_id" \
                    --argjson category "$category_json" \
                    --arg name "$pet_name" \
                    --argjson photoUrls "$photoUrls_json" \
                    --argjson tags "$tags_json" \
                    '$ARGS.named'
    )
    
    # DEBUG:
    printf 'payload is\n%s\n' "$payload_json"
    

    O bashscript acima geraria um documento JSON equivalente ao seguinte (mas em formato compacto):

    {
      "id": "bee001",
      "category": {
        "id": "bee",
        "name": "The bees"
      },
      "name": "Eric \"the half a bee\" II",
      "photoUrls": [
        "url1",
        "url2",
        "url3"
      ],
      "tags": [
        {
          "id": "beauty",
          "name": "high"
        },
        {
          "id": "cost",
          "name": "medium"
        },
        {
          "id": "social",
          "name": "low"
        }
      ]
    }
    
    • 8
  3. Ed Morton
    2024-05-22T20:06:04+08:002024-05-22T20:06:04+08:00

    No momento, você está criando uma string que é uma mistura de formato (nomes de campos, caracteres de estrutura como {and [, etc.) e dados (argumentos para a função). Eu separaria os dados do formato e usaria um documento aqui para preencher esse formato usando -o delimitador de início do documento aqui para fornecer recuo de tabulação (guias iniciais no código onde você preenche a string não aparecem na saída) e quebra automática o aqui delimitador de documento em 's para que todo o formato seja tratado como uma string entre aspas simples para protegê-lo de qualquer possível expansão do shell (por exemplo, Cost $5não expandiria $5como um parâmetro posicional) e, em seguida, deixe "$@"expandir para preencher os valores do espaço reservado %ss no formato que, como benefício colateral, significa que você não precisa de todas as outras variáveis ​​locais:

    $ cat tst.sh
    #!/usr/bin/env bash
    
    add_petstore() {
      local rest_url=$1 fmt pet_to_add
      shift
    
      IFS= read -r -d '' fmt <<-'!'
            {
              "id": %s,
              "category": {
                "id": %s,
                "name": "%s"
              },
              "name": "%s",
              "photoUrls": [
                "%s"
              ],
              "tags": [
                {
                  "id": %s,
                  "name": "%s"
                }
              ]
            }
            !
    
      printf -v pet_to_add "$fmt" "$@"
      echo "$pet_to_add"
    }
    
    add_petstore "http://localhost:8080/pet" "151" "12" "Dogs" "REX" "http://photosofrex/rex_rage.jpg" "1" "ferocious sales"
    

    $ ./tst.sh
    {
      "id": 151,
      "category": {
        "id": 12,
        "name": "Dogs"
      },
      "name": "REX",
      "photoUrls": [
        "http://photosofrex/rex_rage.jpg"
      ],
      "tags": [
        {
          "id": 1,
          "name": "ferocious sales"
        }
      ]
    }
    

    Os espaços iniciais no documento aqui são tabulações, portanto serão ignorados, cortesia do -in <<-. Se você realmente deseja essas guias em sua saída, mude <<-para apenas <<.

    Se você realmente não deseja novas linhas, pet_to_addmas deseja usá-las ao preencher a string de formato, basta alterar isto:

    printf -v pet_to_add "$fmt" "${@}"
    

    para isso:

    printf -v pet_to_add "${fmt//$'\n'}" "${@}"
    

    e então você obterá esta saída:

    $ ./tst.sh
    {  "id": 151,  "category": {    "id": 12,    "name": "Dogs"  },  "name": "REX",  "photoUrls": [    "http://photosofrex/rex_rage.jpg"  ],  "tags": [    {      "id": 1,      "name": "ferocious sales"    }  ]}
    

    e se você quiser comprimir todas as cadeias de espaços em branco contíguos em caracteres em branco únicos, então isto:

    shopt -s extglob
    printf -v pet_to_add "${fmt//+([[:space:]])/ }" "${@}"
    

    produziria esta saída:

    $ ./tst.sh
    { "id": 151, "category": { "id": 12, "name": "Dogs" }, "name": "REX", "photoUrls": [ "http://photosofrex/rex_rage.jpg" ], "tags": [ { "id": 1, "name": "ferocious sales" } ] }
    

    A questão é que você pode massagear trivialmente o formato da maneira que quiser antes de preenchê-lo, pet_to_addsem afetar os dados (e vice-versa), porque dissociamos os dados do formato.

    • 3

relate perguntas

  • exportar variáveis ​​​​env programaticamente, via stdout do comando [duplicado]

  • 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

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

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

    • 7 respostas
  • Martin Hope
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +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

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