อาร์เรย์ JSON เพื่อทุบตีตัวแปรโดยใช้ jq


19

ฉันมีอาร์เรย์ JSON เช่นนั้น:

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

ฉันต้องการวนซ้ำอาร์เรย์นี้โดยใช้ jq ดังนั้นฉันสามารถตั้งค่าคีย์ของแต่ละรายการเป็นชื่อตัวแปรและค่าตามที่เป็นค่า

ตัวอย่าง:

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

สิ่งที่ฉันได้ทำซ้ำไปเรื่อย ๆ จนถึงอาร์เรย์ แต่สร้างสตริง:

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

ผลลัพธ์ใด:

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

ฉันต้องการใช้ตัวแปรเหล่านี้เพิ่มเติมในสคริปต์:

echo ${URL}

แต่สิ่งนี้สะท้อนออกมาเปล่า ๆ ในขณะนี้ ฉันเดาว่าฉันต้องการevalสิ่งใดสิ่งหนึ่งในนั้น แต่ดูเหมือนจะไม่สามารถวางนิ้วมือลงบนมันได้

คำตอบ:


29

รุ่นเดิมของคุณจะไม่ได้ไปจะevalสามารถเพราะชื่อผู้เขียนมีช่องว่างในมัน - มันจะถูกตีความว่าเป็นคำสั่งที่ทำงานDoeกับตัวแปรสภาพแวดล้อมชุดAUTHOR Johnนอกจากนี้ยังไม่จำเป็นที่จะต้องใช้ท่อjqกับตัวเอง - ท่อภายในและ dataflowสามารถเชื่อมต่อตัวกรองที่แตกต่างกันเข้าด้วยกัน

คุณสามารถสร้างโปรแกรม jq เวอร์ชั่นที่เรียบง่ายขึ้น:

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

ผลลัพธ์ใด:

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

ไม่จำเป็นต้องมีmap: .[]จัดการกับการถ่ายวัตถุแต่ละชิ้นในอาร์เรย์ผ่านส่วนที่เหลือของไปป์ไลน์เป็นรายการแยกต่างหากดังนั้นทุกอย่างหลังจากการใช้งานครั้งสุดท้าย|จะถูกนำไปใช้กับแต่ละคนแยกกัน ในตอนท้ายเราเพิ่งรวบรวมสตริงการมอบหมายเชลล์ที่ถูกต้องพร้อมการ+ต่อข้อมูลทั่วไปรวมถึงเครื่องหมายคำพูดรอบค่า

ไปป์ทั้งหมดมีความสำคัญที่นี่ - หากไม่มีคุณจะได้รับข้อความแสดงข้อผิดพลาดที่ไม่ช่วยเหลือซึ่งเป็นส่วนหนึ่งของโปรแกรมที่ได้รับการประเมินในบริบทที่แตกต่างกันอย่างละเอียด

สายนี้คือevalสามารถตราบใดที่ตัวละคร`, $, ขึ้นบรรทัดใหม่และ null จะไม่ปรากฏในข้อมูล:

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

เช่นเคยเมื่อใช้งานevalโปรดระวังว่าคุณไว้วางใจข้อมูลที่คุณได้รับเพราะหากเป็นอันตรายหรือในรูปแบบที่ไม่คาดคิดอาจเป็นสิ่งที่ผิดพลาดมาก


13

จากคำตอบของ @Michael Homer คุณสามารถหลีกเลี่ยงความไม่ปลอดภัยที่อาจเกิดขึ้นได้evalทั้งหมดโดยการอ่านข้อมูลลงในอาเรย์แบบเชื่อมโยง

ตัวอย่างเช่นหากข้อมูล JSON ของคุณอยู่ในไฟล์ชื่อ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]}'"

เอาท์พุท:

$ ./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
นอกจากนี้คุณยังสามารถมอบหมายงานทางอ้อมเพียงอย่างเดียวด้วยdeclare -- “$key=$value”และ$AUTHORทำงานอื่น ๆได้เหมือนต้นฉบับโดยไม่ต้องใช้อาร์เรย์ มันยังคงปลอดภัยกว่า eval แม้ว่าการเปลี่ยนแปลงPATHหรือบางสิ่งบางอย่างยังเป็นไปได้น้อยกว่ารุ่นนี้
Michael Homer

1
ใช่แล้วอาเรย์จะแยกตัวแปรออกเป็นส่วนที่คุณเลือกโดยไม่มีโอกาสที่จะเกิดความสับสนกับตัวแปรสภาพแวดล้อมที่สำคัญโดยไม่ตั้งใจ คุณสามารถทำให้declare --เวอร์ชันของคุณปลอดภัยโดยการเปรียบเทียบคีย์ $ กับรายการของชื่อตัวแปรที่อนุญาต
cas

1

เพิ่งรู้ว่าฉันสามารถวนซ้ำผลลัพธ์และประเมินแต่ละการวนซ้ำ:

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

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

ให้ฉันทำ:

echo ${AUTHOR}
# outputs John Doe

0

ฉันชอบคำแนะนำ @Michel จริงๆ บางครั้งคุณอาจแยกค่าตัวแปรบางอย่างเพื่อดำเนินงานในเซิร์ฟเวอร์นั้นโดยใช้ BASH ดังนั้นตัวแปรที่ต้องการรู้ การใช้วิธีการนี้เป็นวิธีการหลีกเลี่ยงหรือการเรียก jq หลายครั้งเพื่อตั้งค่าต่อตัวแปรหรือแม้กระทั่งการใช้คำสั่งการอ่านที่มีตัวแปรหลายตัวซึ่งบางตัวสามารถใช้ได้และว่างเปล่าซึ่งนำไปสู่การเปลี่ยนค่า (นั่นคือปัญหาของฉัน)

วิธีก่อนหน้าของฉันที่นำไปสู่จะนำไปสู่ข้อผิดพลาดในการเปลี่ยนค่าหาก. svID [] .ID = "" ( svจะได้รับค่าslotID

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

หากคุณดาวน์โหลดวัตถุโดยใช้ curl ต่อไปนี้เป็นวิธีของฉันในการเปลี่ยนชื่อตัวแปรเป็นชื่อที่เรียกง่าย ๆ ว่าดึงข้อมูลจาก data arrays

การใช้ eval และตัวกรองจะแก้ปัญหาด้วยหนึ่งบรรทัดและจะสร้างตัวแปรที่มีชื่อที่ต้องการ

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

ข้อดีในกรณีนี้คือความจริงที่ว่ามันจะกรองเปลี่ยนชื่อจัดรูปแบบตัวแปรทั้งหมดที่ต้องการในขั้นตอนแรก สังเกตว่าในนั้นมี [0] | เป็นเรื่องธรรมดามากหากมีแหล่งที่มาหากจากเซิร์ฟเวอร์ RESTFULL API โดยใช้ GET ข้อมูลการตอบกลับจะเป็น:

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

หากข้อมูลของคุณไม่ได้มาจากอาเรย์ เป็นวัตถุที่ชอบ:

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

เพียงลบดัชนีเริ่มต้น:

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

นี่เป็นคำถามเก่า แต่ฉันรู้สึกแบ่งปันเพราะหายาก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.