Jq kullanarak değişkenleri bashlamak için JSON dizisi


19

Ben böyle bir JSON dizisi var:

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

Değişken adı ve değeri olarak değeri olarak her öğenin anahtarını ayarlayabilirsiniz jq kullanarak bu dizi üzerinden yineleme arıyorum.

Misal:

  • URL = "example.com"
  • YAZAR = "John Doe"
  • CREATED = "2017/10/22"

Şimdiye kadar ne var dizi üzerinde yinelenir ama bir dize oluşturur:

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

Hangi çıktılar:

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

Bu değişkenleri daha fazla komut dosyasında kullanmak için arıyorum:

echo ${URL}

Ama bu şu anda boş bir çıktı yansıtıyor. Sanırım evalorada bir şey falan lazım ama parmağımı koyamıyorum.

Yanıtlar:


30

Orijinal sürümünüz, evalyazar adında boşluk bulunduğundan mümkün olmayacaktır - Doeortam değişkeni olarak AUTHORayarlanmış bir komut çalıştırılıyor olarak yorumlanır John. Ayrıca neredeyse hiçbir zaman jqkendi başına borulama ihtiyacı yoktur - dahili borulama ve veri akışı farklı filtreleri birbirine bağlayabilir.

Jq programının çok daha basit bir versiyonunu yapabilirsiniz:

jq -r '.SITE_DATA | to_entries | .[] | .key + "=\"" + .value + "\""'

hangi çıktılar:

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

A'ya gerek yoktur map: .[]dizideki her nesneyi ardışık düzen olarak ayrı bir öğe olarak ele alır , böylece sonuncudan sonraki her şey |ayrı ayrı uygulanır. Sonunda, +değer etrafında tırnaklar da dahil olmak üzere sıradan birleştirme ile geçerli bir kabuk atama dizesi oluşturuyoruz .

Tüm borular burada önemlidir - onlarsız, programın bölümlerinin farklı bağlamlarda değerlendirildiği oldukça yararsız hata mesajları alırsınız.

Bu dizedir evaluzun karakterlerinin yanı mümkün `, $, satır ve boş verilerinde görünmüyor:

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

Kullanırken evalolduğu gibi, aldığınız verilere güvendiğinizden emin olun, çünkü kötü amaçlıysa veya sadece beklenmedik bir biçimde olursa işler çok yanlış gidebilir.


14

@Michael Homer'ın cevabına dayanarak eval, verileri ilişkilendirilebilir bir dizide okuyarak potansiyel olarak güvenli olmayan bir durumdan kaçınabilirsiniz .

Örneğin, JSON verileriniz şu adlı bir dosyadaysa 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]}'"

Çıktı:

$ ./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'

1
Ayrıca ödevi tek başına dolaylı olarak indirebilir declare -- “$key=$value”ve $AUTHORdizi olmadan orijinaldeki gibi vb. Çalışabilirsiniz. PATHBu versiyondan çok daha az değişiklik olsa da, yine de eval'den daha güvenlidir .
Michael Homer

1
evet, dizi değişkenleri seçtiğiniz bir kapta güzel bir şekilde izole eder - önemli çevre değişkenleriyle yanlışlıkla / kötü amaçlı bir şekilde karışıklık şansı yoktur. declare --$ anahtarını izin verilen değişken adları listesiyle karşılaştırarak sürümünüzü güvenli hale getirebilirsiniz .
cas

1

Sadece sonuçlar üzerinde döngü ve her yineleme değerlendirebilir fark:

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

for key in ${constants}; do
  eval ${key}
done

Yapmamı sağlıyor:

echo ${AUTHOR}
# outputs John Doe

0

@Michel önerisini gerçekten çok seviyorum. Bazen, BASH kullanarak belirli bir sunucuda bir görevi yürütmek için bazı değişkenler değerini gerçekten ayıklayabilirsiniz. Böylece istenen değişkenler bilinir. Bu yaklaşımı kullanan bu, değişken başına bir değer ayarlamak için jq'ye yapılan birden çok çağrıdan kaçınmanın veya bazılarının geçerli ve boş olabileceği birden fazla değişkenle okuma deyimini kullanmanın bir değer kaymasına (bu benim sorunumdu) yol açmanın yoludur

Kurşun bir değer kayması hatası .svID [] .kimlik eğer = "" (yol açacağı önceki yaklaşım sv alacak slotID değeri

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

Nesneyi curl kullanarak indirdiyseniz, veri dizilerinden veri ayıklama olarak bazı değişkenleri kolay bir adla yeniden adlandırma yaklaşımım

eval ve filtreleri kullanmak sorunu bir satırla çözecek ve istenen isimde değişkenler üretecektir

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 )"  

Bu durumda avantaj, ilk adımda istenen tüm değişkenleri filtreleyecek, yeniden adlandıracak, biçimlendirecek olmasıdır. İçinde olduğunu gözlemleyin. [0] | GET kullanan bir RESTFULL API sunucusundan kaynak yanıt verisi aşağıdaki gibi olması çok yaygındır:

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

Verileriniz bir diziden değilse, yani. şunun gibi bir nesnedir:

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

sadece başlangıç ​​dizinini kaldırın:

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 )"  

Bu eski bir soru, ama paylaşmak zor hissettim, çünkü bulmak zor

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.