![jq를 사용하여 JSON 배열을 bash 변수로 변환](https://linux55.com/image/124647/jq%EB%A5%BC%20%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC%20JSON%20%EB%B0%B0%EC%97%B4%EC%9D%84%20bash%20%EB%B3%80%EC%88%98%EB%A1%9C%20%EB%B3%80%ED%99%98.png)
다음과 같은 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
실행하는 것으로 해석됩니다 . 실제로 자체적으로 파이프할 필요도 없습니다 .Doe
AUTHOR
John
jq
내부 파이프라인 및 데이터 흐름다양한 필터를 함께 연결할 수 있습니다.
이 모든 것은 입력 데이터를 완전히 신뢰하는 경우에만 의미가 있습니다(예: 제어하는 도구에 의해 생성된 데이터). 몇 가지 가능한 문제가 아래에 자세히 설명되어 있지만 데이터 자체는 확실히 현재 예상하는 형식이라고 가정하겠습니다.
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 )"