Mảng JSON để bash biến bằng jq


18

Tôi đã có một mảng JSON như vậy:

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

Tôi đang tìm cách lặp lại mảng này bằng jq để tôi có thể đặt khóa của từng mục làm tên biến và giá trị làm giá trị của nó.

Thí dụ:

  • URL = "example.com"
  • TÁC GIẢ = "John Doe"
  • TẠO = "22/10/2017"

Những gì tôi đã có cho đến nay lặp đi lặp lại trên mảng nhưng tạo ra một chuỗi:

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

Đầu ra nào:

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

Tôi đang tìm cách sử dụng các biến này trong kịch bản:

echo ${URL}

Nhưng tiếng vang này là một đầu ra trống rỗng vào lúc này. Tôi đoán tôi cần một evalcái gì đó trong đó nhưng dường như không thể đặt ngón tay lên nó.

Câu trả lời:


28

Phiên bản gốc của bạn sẽ không thể thực hiện được evalvì tên tác giả có khoảng trắng trong đó - nó sẽ được hiểu là chạy một lệnh Doevới biến môi trường AUTHORđược đặt thành John. Hầu như không bao giờ cần phải tự lắp ống jq- đường ống & dataflow bên trong có thể kết nối các bộ lọc khác nhau lại với nhau.

Bạn có thể tạo một phiên bản đơn giản hơn nhiều của chương trình jq:

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

đầu ra nào:

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

Không cần phải có map: .[]liên quan đến việc đưa từng đối tượng trong mảng qua phần còn lại của đường ống làm một mục riêng biệt , vì vậy mọi thứ sau lần cuối |được áp dụng riêng cho từng đối tượng. Cuối cùng, chúng ta chỉ cần lắp ráp một chuỗi gán vỏ hợp lệ với +phép nối thông thường , bao gồm các trích dẫn xung quanh giá trị.

Tất cả các vấn đề quan trọng ở đây - không có chúng, bạn nhận được các thông báo lỗi khá vô ích, trong đó các phần của chương trình được đánh giá trong các bối cảnh khác nhau tinh tế.

Chuỗi này là evalcó thể miễn là các nhân vật `, $, xuống dòng và null không xuất hiện trong các dữ liệu:

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

Khi sử dụng eval, hãy cẩn thận rằng bạn tin tưởng vào dữ liệu bạn nhận được, vì nếu nó độc hại hoặc chỉ ở định dạng không mong muốn, mọi thứ có thể rất sai.


13

Dựa trên câu trả lời của @Michael Homer, bạn có thể tránh evalhoàn toàn không an toàn bằng cách đọc dữ liệu vào một mảng kết hợp.

Ví dụ: nếu dữ liệu JSON của bạn nằm trong một tệp có tên 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]}'"

Đầu ra:

$ ./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
Bạn cũng có thể gián tiếp chuyển nhượng một mình với declare -- “$key=$value”và có $AUTHORvv hoạt động như trong bản gốc, không có mảng. Nó vẫn an toàn hơn eval, mặc dù thay đổi PATHhoặc một cái gì đó vẫn có thể ít hơn so với phiên bản này.
Michael Homer

1
yeah, mảng độc lập cô lập các biến vào một thùng chứa mà bạn chọn - không có khả năng vô tình / gây rối với các biến môi trường quan trọng. bạn có thể làm cho declare --phiên bản của mình an toàn bằng cách so sánh $ key với danh sách các tên biến được phép.
cas

1

Chỉ cần nhận ra rằng tôi có thể lặp lại kết quả và đánh giá mỗi lần lặp:

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

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

Cho phép tôi làm:

echo ${AUTHOR}
# outputs John Doe

0

Tôi thực sự thích đề xuất @Michel. Đôi khi, bạn thực sự có thể chỉ trích xuất một số giá trị biến để thực thi một tác vụ trong máy chủ cụ thể đó bằng BASH. Vì vậy, các biến mong muốn được biết. Cách sử dụng phương pháp này là cách để tránh hoặc nhiều cuộc gọi đến jq để đặt giá trị cho mỗi biến hoặc thậm chí sử dụng câu lệnh đọc với nhiều biến trong đó một số có thể hợp lệ và trống, dẫn đến thay đổi giá trị (đó là vấn đề của tôi)

Cách tiếp cận trước đó của tôi dẫn đến lỗi dịch chuyển giá trị nếu .svID [] .ID = "" ( sv sẽ nhận được giá trị slotID

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

Nếu bạn đã tải xuống đối tượng bằng curl, đây là cách tiếp cận của tôi để đổi tên một số biến thành tên thân thiện dưới dạng trích xuất dữ liệu từ mảng dữ liệu

sử dụng eval và bộ lọc sẽ giải quyết vấn đề với một dòng và sẽ tạo ra các biến có tên mong muốn

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

Ưu điểm trong trường hợp này là thực tế là nó sẽ lọc, đổi tên, định dạng tất cả các biến mong muốn trong bước đầu tiên. Quan sát rằng trong đó có. [0] | đó là điều rất phổ biến nếu có nguồn nếu từ máy chủ API RESTFULL sử dụng GET, dữ liệu phản hồi là:

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

Nếu dữ liệu của bạn không phải từ một mảng, tức là. là một đối tượng như:

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

chỉ cần xóa chỉ mục ban đầu:

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

Đây là một câu hỏi cũ, nhưng tôi cảm thấy chia sẻ, vì nó rất khó tìm

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.