英文:
jq loop over objects and use shell command output to add a field
问题
我正在尝试迭代遍历JSON并为JSON中的每个对象添加一个字段。我在Gitlab管道阶段中执行此操作,但我认为这不重要。
JSON如下:
{
"service10": {
"path_to_file" : "service10.txt"
},
"service192": {
"path_to_file" : "service192.txt"
}
}
我正在使用path_to_file
来检查Gitlab仓库中是否存在该文件,并且我需要保存HTTP响应,目前的代码如下:
check_files_existence:
stage: update-external-repo
script:
- apt-get update --fix-missing
- apt-get install jq -y
- HTTP_RESPONSE_CODES=()
# Check for file
- |-
for FILE_TO_CHECK in $(jq -r '.[].path_to_file ' $JSON_TO_READ); do
last_status_code=($(curl [omitted]))
HTTP_RESPONSE_CODES+=($last_status_code)
done
# Write array of responses into artifact
- echo "${HTTP_RESPONSE_CODES[@]}" >> ./http_response_code.txt
artifacts:
paths:
- ./http_response_code.txt
expire_in: 10 min
这将生成一个包含以空格分隔的HTTP响应的.txt文件。
我想要在循环遍历服务时将HTTP响应添加到每个服务中,以便JSON看起来像这样:
{
"service10": {
"path_to_file" : "service10.txt",
"http_response": 200
},
"service192": {
"path_to_file" : "service192.txt",
"http_response": 404
}
}
然后,我将将其导出为构件,以在Gitlab管道的另一个阶段中使用。
使用上面的for循环,我可以获取文件路径,但然后看起来我无法更新JSON。如果我使用类似以下的内容:
for OBJECT in $(jq -r '.[] ' $JSON_TO_READ)
我无法读取文件路径,因为使用
jq '.path_to_file $OBJECT'
会返回错误。
有办法做到这一点吗?
英文:
I'm trying to iterate over a JSON and add a field to each object in the JSON.
I'm doing this in a Gitlab pipeline stage but I don't think is important.
The JSON is this:
{
"service10": {
"path_to_file" : "service10.txt"
},
"service192": {
"path_to_file" : "service192.txt"
}
}
What I'm doing is using the path_to_file
to check in a Gitlab repo if that file exists and I need to save the http response, the code now looks as follows:
check_files_existence:
stage: update-external-repo
script:
- apt-get update --fix-missing
- apt-get install jq -y
- HTTP_RESPONSE_CODES=()
# Check for file
- |-
for FILE_TO_CHECK in $(jq -r '.[].path_to_file ' $JSON_TO_READ); do
last_status_code=($(curl [omitted]))
HTTP_RESPONSE_CODES+=($last_status_code)
done
# Write array of responses into artifact
- echo "${HTTP_RESPONSE_CODES[@]}" >> ./http_response_code.txt
artifacts:
paths:
- ./http_response_code.txt
expire_in: 10 min
This generates a .txt file with the http responses separated by a whitespace.
What I want to do is add a field to each service while I loop over them with the http_response, so the JSON will be like:
{
"service10": {
"path_to_file" : "service10.txt",
"http_response": 200
},
"service192": {
"path_to_file" : "service192.txt",
"http_response": 404
}
}
And then I'm going to export this an artifact to use it in another stage in my Gitlab Pipeline.
Using the above for loop I can get the file path but then it looks like I cannot update the JSON, if I use something like
for OBJECT in $(jq -r '.[] ' $JSON_TO_READ)
I cannot read the file path because using
jq '.path_to_file $OBJECT'
returns an error.
Is there a way to do this?
Thanks.
答案1
得分: 0
这可能需要两个独立的jq副本。假设您的输入文件已更改为有效的JSON:
jq -r 'to_entries[] | [.key, .value.path_to_file] | @tsv' <"$JSON_TO_READ" \
| while IFS=$'\t' read -r key value; do
# 在您的真实系统中取消注释以下行
(( ++http_status )) # 用于在不调用curl的情况下进行测试
# 取消注释下一行并在您的真实系统中替换URL
# http_status=$(curl -s -o /dev/null -w "%{http_code}" "http://example.com/$key") || continue
printf '%s\t%s\n' "$key" "$http_status"
done \
| jq -Rn --slurpfile input_file "$JSON_TO_READ" '
reduce inputs as $inline
($input_file[0];
(($inline | split("\t")) as $pieces |
.[$pieces[0]].http_response = ($pieces[1])))'
分解此shell管道:
-
首先,有一个生成带有键和值的制表符分隔流的jq副本。此处的条目将如下所示:
service10<TAB>service10.txt
-
接下来,我们有一个shell管道(运行BashFAQ #1循环),读取这两个变量,并运行curl(或在上面的示例中,仅将递增的数字作为状态代码)。以确定HTTP状态代码:
service10<TAB>200
-
最后,我们有另一个jq的副本,通过
--slurpfile
接受您的输入文件,并从stdin读取制表符分隔的服务键和HTTP状态代码流,使用一个减少器来逐渐更新状态文件以获取新值(因为--slurpfile
包含在文件中找到的JSON文档的列表,而不仅仅是单个文档,所以我们引用$input_file[0]
以引用文件中的第一个文档 - 唯一的文档)。
英文:
This probably calls for two separate copies of jq. Assuming your input file were changed to be valid JSON:
jq -r 'to_entries[] | [.key, .value.path_to_file] | @tsv' <"$JSON_TO_READ" \
| while IFS=$'\t' read -r key value; do
# comment this next line in your real system
(( ++http_status )) # stand-in so this can be tested without calling curl
# uncomment this next line and replace the url in your real system
#http_status=$(curl -s -o /dev/null -w "%{http_code}" "http://example.com/$key") || continue
printf '%s\t%s\n' "$key" "$http_status"
done \
| jq -Rn --slurpfile input_file "$JSON_TO_READ" '
reduce inputs as $inline
($input_file[0];
(($inline | split("\t")) as $pieces |
.[$pieces[0]].http_response = ($pieces[1])))'
Breaking down the shell pipeline:
-
First, we have a copy of jq that emits a tab-separated stream with your key and value. An entry here would look like:
service10<TAB>service10.txt
-
Next, we have a shell pipeline (running a BashFAQ #1 loop) that reads these two variables, and runs curl (or in the example above, just adds an incrementing number as the status code) to determine the HTTP status code:
service10<TAB>200
-
Finally, we have another copy of jq that accepts your input file via
--slurpfile
, and reads the stream of tab-separated service keys and HTTP status codes from stdin, using a reducer to incrementally update the status file as new values come in. (Because--slurpfile
contains a list of JSON documents found in the file, not just a single document, we're referring to$input_file[0]
to refer to the first -- and only -- document in the file).
答案2
得分: 0
如果您的文件不太大,并且不介意多次阅读相同的输入文件,那么这是另一种可能适用的解决方案:
- 计算对象中的键值对数量 链接
- 从对象中按索引提取单个条目
- 从条目中获取
path_to_file
的值 - 调用 curl
- 将
http_response
添加到条目 - 使用
jq -s
(--slurp
) 从条目重建对象
input='input.json'
length="$(jq 'length' "$input")"
for i in $(seq "$length"); do
entry="$(jq --argjson idx "$i" 'to_entries[$idx-1]' "$input")"
path="$(printf '%s' "$entry" | jq -r '.value.path_to_file')"
last_status_code="$(curl ... "$path")"
printf '%s' "$entry" | jq --argjson status "$last_status_code" '.value.http_response = $status'
done | jq -s 'from_entries'
英文:
If your file isn't too big and you don't mind reading the same input file multiple times, then here is another solution that should work:
- Count the number of key-value-pairs in the object
- Extract single entry by index from object
- Get
path_to_file
value from entry - Call curl
- Add
http_response
to entry - Use
jq -s
(--slurp
) to reconstruct the object from its entries
input='input.json'
length="$(jq 'length' "$input")"
for i in $(seq "$length"); do
entry="$(jq --argjson idx "$i" 'to_entries[$idx-1]' "$input")"
path="$(printf '%s' "$entry" | jq -r '.value.path_to_file')"
last_status_code="$(curl ... "$path")"
printf '%s' "$entry" | jq --argjson status "$last_status_code" '.value.http_response = $status'
done | jq -s 'from_entries'
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论