jq를 사용하여 JSON 배열을 bash 변수로 변환

jq를 사용하여 JSON 배열을 bash 변수로 변환

다음과 같은 JSON 배열이 있습니다.

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

각 항목의 키를 변수 이름으로 설정하고 값을 해당 값으로 설정할 수 있도록 jq를 사용하여 이 배열을 반복하고 싶습니다.

예:

  • URL="example.com"
  • 저자 = "John Doe"
  • 생성됨="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손에 쥘 수가 없는 것 같아요.

답변1

작성자 이름에 공백이 포함되어 있으므로 원래 버전은 실행되지 않습니다 . 환경 변수가 로 설정된 명령을 eval실행하는 것으로 해석됩니다 . 실제로 자체적으로 파이프할 필요도 없습니다 .DoeAUTHORJohnjq내부 파이프라인 및 데이터 흐름다양한 필터를 함께 연결할 수 있습니다.

이 모든 것은 입력 데이터를 완전히 신뢰하는 경우에만 의미가 있습니다(예: 제어하는 ​​도구에 의해 생성된 데이터). 몇 가지 가능한 문제가 아래에 자세히 설명되어 있지만 데이터 자체는 확실히 현재 예상하는 형식이라고 가정하겠습니다.

jq 프로그램의 더 간단한 버전을 만들 수 있습니다.

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

출력:

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

불필요한 map:.[]파이프라인의 나머지 부분을 통해 배열의 각 개체를 별도의 항목으로 처리합니다., 따라서 마지막 항목 이후의 모든 내용은 |각각에 개별적으로 적용됩니다. 마지막으로 +적절한 문자열을 포함하여 일반적인 연결을 사용하여 유효한 쉘 할당 문자열을 간단히 조합합니다.값을 인용하고 이스케이프 처리하세요.@sh.

여기에서는 모든 파이프가 중요합니다. 파이프가 없으면 프로그램의 다양한 부분이 미묘하게 다른 컨텍스트에서 평가되는 쓸모 없는 오류 메시지가 표시됩니다.

이 문자열 eval입력 데이터를 완전히 신뢰하는 경우원하는 효과를 얻으십시오.

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

항상 그렇듯이 eval, 데이터가 악의적이거나 예상치 못한 형식인 경우 심각한 오류가 발생할 수 있으므로 신뢰하는 데이터에 주의하세요. 특히, 만약열쇠$실행 중인 명령을 생성하는 셸 메타 문자(예: 또는 공백)를 포함합니다 . 예를 들어 PATH실수로 환경 변수를 덮어쓸 수도 있습니다 .

데이터를 신뢰할 수 없다면 이 작업을 전혀 수행하지 않거나 먼저 원하는 키만 포함하도록 객체를 필터링하세요.

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

다음과 같은 경우에도 문제가 발생할 수 있습니다.배열이므로 .value | tostring | @sh더 좋을 것입니다. 그러나 이 경고 목록은 좋은 이유가 될 수 있습니다.아니요이 모든 것을 먼저 수행하십시오.


다음을 생성할 수도 있습니다.연관 배열대신 키와 값이 모두 인용됩니다.

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

그 다음에는 ${data[CREATED]}키나 값의 내용이 무엇이든 생성 날짜 등을 포함합니다. 이는 가장 안전한 옵션이지만 내보낼 수 있는 최상위 수준 변수가 생성되지는 않습니다. 값이 배열인 경우 여전히 Bash 구문 오류가 발생할 수 있습니다. 객체인 경우 여전히 jq 오류가 발생할 수 있지만 코드가 실행되거나 아무것도 덮어쓰지 않습니다.

답변2

@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'

답변3

결과를 반복하고 각 반복을 평가할 수 있다는 것을 방금 깨달았습니다.

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

답변4

@Michel의 제안이 정말 마음에 듭니다. 때로는 특정 서버에서 작업을 수행하기 위해 Bash를 사용하기 위해 실제로 일부 변수 값을 추출할 수도 있습니다. 따라서 필요한 변수가 알려진 경우 이 접근 방식을 사용하면 jq각 변수의 값을 설정하기 위한 여러 번의 호출을 피할 수 있습니다. 또는 read여러 변수가 포함된 명령문을 사용할 수도 있습니다. 그 중 일부는 유효하거나 비어 있을 수 있으므로 값이 이동하게 됩니다(예: 내 문제).

이전 접근 방식에서는 값 변환 오류가 발생했습니다. .svID[ ].ID=""인 경우 sv다음과 같은 오류가 발생했습니다.슬롯 번호값.

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

컬을 사용하여 객체를 다운로드하는 경우 데이터 배열에서 데이터를 추출하기 위해 일부 변수의 이름을 친숙한 이름으로 바꾸는 방법은 다음과 같습니다.

eval과 filter를 사용하는 한 줄만 있으면 문제가 해결되고 원하는 이름의 변수가 생성됩니다.

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] |소스가 GET을 사용하는 RESTful API 서버에서 오는 경우 응답 데이터는 다음과 같습니다.

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

관련 정보