반환된 데이터를 구문 분석하기 위해 bash
with 를 사용하고 있습니다 .jq
https://ipinfo.io/json연관 배열로. 거의 작업을 수행하는 훌륭한 예를 찾았습니다@https://gist.github.com/awesome/b3f65084c70264e87be3e72ee8abd0e5
코드는 대부분의 데이터를 구문 분석할 수 있지만 값에 여러 단어로 구성된 문자열이 포함되어 있으면 실패합니다. 문제는 따옴표를 올바른 위치에 넣는 것과 관련이 있다고 생각하지만 어디에 있는지 모르겠습니다. 나는 문서를 보고 jq
일반적인 아이디어를 얻었지만 세부 사항이 나를 당황하게 했습니다. jq
파이프, 템플릿 및 축소 간의 상호 작용을 이해하는 데 약간의 어려움이 있습니다 .(이번이 처음으로 사용해 jq
보지만 꽤 확실합니다 regex
.)
내 코드 버전은 다음과 같습니다
locationResult=$(curl -s 'https://ipinfo.io/json')
arrayAsString=$(echo "$locationResult" | jq --raw-output '. | to_entries | map("[\(.key)]=\(.value)") | reduce .[] as $item ("associativeArray=("; . + $item + " ") + ")"')
declare -A "$arrayAsString"
echo ${associativeArray[org]}
내 위치의 경우 org
여러 단어로 된 회사 이름이 반환되어 declare -A "$arrayAsString"
경고/오류가 생성되고 echo ${associativeArray[org]}
필드의 첫 번째 단어만 생성됩니다 org
.
나는 jq
그 결과를 바탕으로 인용하려고 노력한다.json 값에 공백이 포함된 경우 bash 배열에 jq 출력 할당문제가 있지만 작동하지 않았습니다.
어떤 도움이라도 대단히 감사하겠습니다.
답변1
언제든지 다음과 같이 할 수 있습니다.
typeset -A ipinfo
while IFS= read -rd '' key && IFS= read -rd '' value; do
ipinfo[$key]=$value
done < <(
set -o pipefail
curl -s https://ipinfo.io/json |
jq -j '
to_entries[] |
[.key, .value | tostring] |
map(gsub("\u0000"; "") + "\u0000") |
add'
)
wait "$!" || exit # if curl or jq failed. Needs bash 4.4 or newer.
즉, jq
출력 NUL로 구분된 키와 값을 가져오고(문자열로 변환하고 NUL 문자를 제거함(bash는 해당 변수에 이를 저장할 수 없음)) IFS= read -rd ''
.
이는 NUL 문자와 널 키를 포함하는 것을 제외한 임의의 키와 값을 허용합니다(불행한 bash 연관 배열의 제한으로). 어느 날 null 키가 있는 요소가 추가되면 스크립트가 중단되므로 ipinfo.io
null 키가 있는 멤버를 명시적으로 제외해야 할 수도 있습니다. 또한 bash(ksh93과 반대)는 복잡/재귀 데이터 구조를 지원하지 않기 때문에 값을 문자열로 변환하고 있습니다.
zsh
대신 사용하십시오 bash
:
typeset -A ipinfo
ipinfo=(
${(0)"$(
set -o pipefail
curl -s https://ipinfo.io/json |
jq -j '
to_entries |
map([.key, .value | tostring]) |
flatten |
map(gsub("\u0000"; "")) |
join("\u0000")'
)"}
) || exit
typeset -p ipinfo
zsh
Null 키는 실제로 지원되며 키와 값 목록에서 연관 배열 전체를 안전하게 할당하는 올바른 방법이 있습니다. 변수에 NUL을 저장하는 것을 지원하지만 여기서는 NUL을 구분 기호로 사용하므로 값에서 NUL을 제거해야 합니다.
Ksh93(쉘 bash가 많은 API를 복사함)은 복잡한 데이터 구조를 지원하고 ksh93v-beta 버전은 json 구문 분석에 대한 실험적 지원도 제공하지만 여전히 버그가 있습니다. json 지원은 이를 기반으로 한 ksh2020에서 제거되었으며(현재는 더 이상 사용되지 않음) ksh93u+를 기반으로 여전히 유지 관리되는 ksh93 버전에도 지원되지 않으므로 수동으로 구문 분석을 구현해야 합니다. ksh93은 또한 변수에 NUL을 저장하는 것을 지원하지 않습니다. 단, 여기에서 활용할 수 있는 Base64 인코딩 간 변환을 위한 도우미가 있습니다.
귀하의 접근 방식에는 임의 명령 실행 취약점이 발생합니다. 쉘 파서가 인터넷의 임의 데이터에 노출되는 것을 원하지 않습니다. (코드로 입력하기에 적합한 형식으로 값을 인코딩하도록 설계된 ) jq
을 사용하더라도 데이터가 예상 유형이 아닌 경우 문제를 간과하기 쉽습니다. 또는 / 와 같은 것들은 매우 큰 알람 벨을 울려야 합니다.@sh
sh
eval "$untrusteddata"
typeset
declare ... "$untrusteddata"
여기서는 쉘 대신 적절한 프로그래밍 언어, 특히 bash
.
답변2
나는 이것을 할 수 있습니다 :
declare -A arr
eval "$(
curl -s https://ipinfo.io/json |
jq -r 'to_entries[] | @sh "arr[\(.key|tostring)]=\(.value|tostring)"'
)"
이는 쉘을 참조하는 키와 값을 사용하고 @sh
, 키와 값을 문자열로 명시적으로 변환 tostring
(예상치 못한 유형의 값을 처리(문자열화))하는 방법을 사용합니다. 표현식은 jq
쉘 코드를 생성하고 쉘은 이를 평가하여 arr
연관 배열을 생성합니다.
스테판 차젤라스(Stéphane Chazelas)는 책에서 모닝콜에 대해 썼습니다.그의 대답, 그러나 입력이 유효한 JSON이라고 가정하면 실제로 이 사례를 찾을 수 없습니다.
답변3
map
함수 에서는 jq
키와 값을 작은따옴표( )로 묶어야 합니다 '
. 귀하의 경우 공백은 값에만 표시되지만 따옴표 안에 키를 포함하면 공백이 포함된 키도 처리됩니다.
'"'"'
키와 값 앞뒤에 다음을 추가 해야 합니다 .
arrayAsString=$(echo "$locationResult" | jq --raw-output '. | to_entries | map("['"'"'\(.key)'"'"']='"'"'\(.value)'"'"'") | reduce .[] as $item ("associativeArray=("; . + $item + " ") + ")"')
$
또는 명령의 첫 번째 작은따옴표 앞에 달러 기호를 추가하고 jq
키와 값을 \'
.
arrayAsString=$(echo "$locationResult" | jq --raw-output $'. | to_entries | map("[\'\(.key)\']=\'\(.value)\'") | reduce .[] as $item ("associativeArray=("; . + $item + " ") + ")"')
값의 키 자체에 작은따옴표가 포함되어 있으면 이 해결 방법이 작동하지 않습니다. 하지만 그 외에는 괜찮을 것입니다.
답변4
우선, 내 질문에 잘 작동하고 작업하도록 영감을 준 @StéphaneChazelas에게 감사의 말씀을 전하고 싶습니다 box
.
키에 대한 우려는 null
.del(."") |
to_entries[]
@StéphaneChazelas의 솔루션이 작동하는 동안 루프와 스레드가 필요하지 않고 가장 중요한 것은 입력을 한 번만 보는 깔끔한 솔루션을 원합니다. 일련의 실험을 통해 저는 다음과 같은 코드를 생성할 수 있었습니다. 이 코드는 제가 생각하기에 더 효율적이라고 생각되는 방식으로 동일한 결과를 얻었습니다.
awesome
(이 코드는 제가 OP에 링크한 예제의 작성자인 @StéphaneChazelas와 github의 아이디어에 영향을 받았습니다 .)
parsed=$(echo "$theInput" | jq --raw-output ' . | del(."") | to_entries | map("[\(.key)]=\u0022\(.value)\u0022") | reduce .[] as $item ("locationData=("; . + $item + " ") + ")"')
echo -e "parsed=\n$parsed"
declare -A "$parsed"
echo -e "locationData[org]=\n${locationData[org]}"
echo -e "locationData[city]=\n${locationData[city]}"
echo -e "locationData[\"loc coord\"]=\n${locationData['loc coord']}\n\n"
인라인 호출 echo "$theInput"
로 대체될 수 있습니다 . 위 코드에서 변수를 사용하기 전에 bash 변수를 호출했습니다. 위의 코드는 가능한 한 "나쁜" 상태로 만들기 위해 "도살"된 다음 샘플 데이터를 사용하여 작동합니다.curl
curl
null
열쇠가 있는 입구 가 있습니다 .- 키 중 하나에 해당 값이 없습니다.
- 및 이스케이프된 따옴표를 포함
[
하는 여러 단어로 구성된 문자열이 있습니다 .]
'
\"
key
@aviro가 제안한 대로 공백으로 구분된 두 단어로 구성된 를 포함합니다 .- 지금까지 발견한 유일한 문제는 값에 이스케이프 처리되지 않은 따옴표를 포함할 수 없어 런타임 오류가 발생한다는 것입니다.
theInput=$(cat <<EOF
{
"ip": "W.X.Y.Z",
"": "123.456.789.ABC",
"city": "Some City",
"town": "",
"region": "My State",
"country": "My Country",
"loc coord": "0.000,0.0000",
"org": "Company's [Short] \"Corporation\" Name",
"postal": "12345",
"timezone": "Some/Timezone",
"readme": "https://ipinfo.io/missingauth"
}
EOF
)
내 주장과 방법론에 허점을 지적해 주시기 바랍니다.