英文:
Each item in a loop is an array. How do I use one item in the array when looping?
问题
Here's the translated code section:
我有一个任务,用来循环遍历一些JSON数据(这是有关Confluence页面及其附件的信息,来自Confluence REST API):
```json
{
"results": [
{
"title": "page0",
"children": {
"attachment": {
"results": [
{
"title": "page0attachment0",
"history": {}
},
{
"title": "page0attachment1",
"history": {}
}
]
}
}
},
{
"title": "page1",
"children": {
"attachment": {
"results": [
{
"title": "page1attachment0",
"history": {}
},
{
"title": "page1attachment1",
"history": {}
}
]
}
}
}
]
}
这是我正在使用的循环过滤器:
var.json.results | subelements('children.attachment.results')
这个代码运行良好,但在循环时,页面信息是第一个元素,然后附件信息是第二个元素。
例如,在循环中如果我调用 {{ item.0.title }}
,我会得到以下结果:
"msg": "page0"
"msg": "page0"
"msg": "page1"
"msg": "page1"
如果我调用 {{ item.1.title }}
,我会得到:
"msg": "page0attachment0"
"msg": "page0attachment1"
"msg": "page1attachment0"
"msg": "page2attachment1"
因此,循环包含一个数组,页面信息是第一个元素,每个附件都是第二个元素。
再次强调,它在普通的Playbook中正常工作,例如:
- name: 获取项目信息
amazon.aws.s3_object_info:
region: "{{ region }}"
bucket_name: "{{ bucket }}"
object_name: "file/path/{{ item.1.title }}"
loop: "{{ var.json.results | subelements('children.attachment.results') }}"
问题在于我希望任务被分成一个tasks
目录,并使用include_tasks
,像这样:
- name: 获取项目信息
include_tasks: get_s3_object_info.yml
loop: "{{ var.json.results | subelements('children.attachment.results') }}"
因此,由于我仍然需要在循环中捕获第二个元素,我不想在get_s3_object_info.yml
任务中编写item.1.title
,因为我希望能够在其他地方使用该任务,所以我希望只调用item.title
。
注意,我还需要访问history
属性,这就是为什么我不能只获取附件标题的平面列表,我需要遍历包含title
和history
属性的对象列表。
最重要的是,无论是通过修改jinja模板,还是使用loop_control
或其他方式,如何将item.1
变为item
?
也许类似var.json.results | subelements('children.attachment.results') | foreach(index=1)
的东西?
此外,每个项目都有一个type
属性:page
、attachment
等。是否有办法添加一个类似where(type='attachment')
的条件?
<details>
<summary>英文:</summary>
I have a task that loops through some JSON data (it's information about Confluence pages and their attachments, from the Confluence REST API):
```json
{
"results": [
{
"title": "page0",
"children": {
"attachment": {
"results": [
{
"title": "page0attachment0",
"history": {}
},
{
"title": "page0attachment1",
"history": {}
}
]
}
}
},
{
"title": "page1",
"children": {
"attachment": {
"results": [
{
"title": "page1attachment0",
"history": {}
},
{
"title": "page1attachment1",
"history": {}
}
]
}
}
}
]
}
This is the filter I'm using to loop:
var.json.results | subelements('children.attachment.results')
This works fine, but when looping for some reason, the page information is the first element then the attachment information is the second element.
For example, in the loop if I call {{ item.0.title }}
I'll get the following:
"msg": "page0"
"msg": "page0"
"msg": "page1"
"msg": "page1"
And if I call {{ item.1.title }}
I get:
"msg": "page0attachment0"
"msg": "page0attachment1"
"msg": "page1attachment0"
"msg": "page2attachment1"
So it appears that the loop contains an array with the page information as the first element, repeated for each attachment and the attachment as the second element.
Again, it's working fine in a regular playbook with a task like this:
- name: Get item info
amazon.aws.s3_object_info:
region: "{{ region }}"
bucket_name: "{{ bucket }}"
object_name: "file/path/{{ item.1.title }}"
loop: "{{ var.json.results | subelements('children.attachment.results') }}"
The problem is I want the task to be separated into a tasks
directory and use include_tasks
like this:
- name: Get item info
include_tasks: get_s3_object_info.yml
loop: "{{ var.json.results | subelements('children.attachment.results') }}"
So, since I still need to capture the second element within the loop I don't want to have to write the item.1.title
thing into the get_s3_object_info.yml
task because I want to be able to use that task elsewhere so I want to just call item.title
within that task.
NOTE I also need to access the history
attribute, which is why I can't just get a flat list of attachment titles, I need to iterate through a list of objects that contain both the title
and history
attributes.
Bottom line--whether by modifying the jinja template, or using loop_control
or otherwise, how can I turn item.1
into just item
?
Maybe something like var.json.results | subelements('children.attachment.results') | foreach(index=1)
or something like that?
Also, each item has a type
attribute: page
, attachment
, etc. Is there a way I can add a where(type='attachment')
or something similar?
答案1
得分: 2
Here's the translated content:
预备说明: 使用名为 var
的变量绝不是一个好主意,因为它没有意义,可能与Ansible/Python中的其他名称冲突。我在下面的答案中将其更改为 confuence_api_page_request
。根据您的需要进行适应。
在您的包含任务中,只需按照您要使用的方式精确描述变量,例如在 get_s3_object_info.yml
中:
- name: 从S3获取附件信息
amazon.aws.s3_object_info:
region: "{{ region }}"
bucket_name: "{{ bucket }}"
object_name: "file/path/{{ attachment.title }}"
然后将变量按照需要传递给您的包含任务文件:
- name: 获取附件信息
vars:
attachment: "{{ item.1 }}"
include_tasks: get_s3_object_info.yml
loop: "{{ confuence_api_page_request.json.results
| subelements('children.attachment.results') }}"
此外,由于您在上述任务中没有在任何地方使用顶层元素页面信息,您可以轻松地从您的JSON中提取它作为一个单一的列表,并直接在附件信息上进行循环。保持上述相同的包含文件,只需更改 include_tasks
循环为:
- name: 获取附件信息
include_tasks: get_s3_object_info.yml
loop: "{{ confuence_api_page_request.json.results
| map(attribute='children.attachment.results') | flatten }}"
loop_control:
loop_var: attachment
在所有情况下,您可以使用 attachment.history
从包含文件中访问历史记录。
还请注意,第二个解决方案将允许您在任何包含的任务中使用默认的 item
循环变量(如果需要),因为包含循环变量不会与其冲突。
英文:
preliminary note: having a variable called var
is anything but a good idea as it is not meaningful and might conflict with other names in ansible/python. I changed the name to confuence_api_page_request
in the below answer. Adapt to your need
In your included task, just describe the variable exactly as you want to use it, e.g. in get_s3_object_info.yml
:
- name: Get attachment info from S3
amazon.aws.s3_object_info:
region: "{{ region }}"
bucket_name: "{{ bucket }}"
object_name: "file/path/{{ attachment.title }}"
Then pass the variable exactly as needed to your included task file:
- name: Get attachment info
vars:
attachment: "{{ item.1 }}"
include_tasks: get_s3_object_info.yml
loop: "{{ confuence_api_page_request.json.results
| subelements('children.attachment.results') }}"
Moreover, since you are not using the top element page information anywhere in the above tasks, you can easily extract that as a single list from your json and loop directly on the attachement information. Keeping the exact same include file above, just change the include_tasks
loop to:
- name: Get attachment info
include_tasks: get_s3_object_info.yml
loop: "{{ confuence_api_page_request.json.results
| map(attribute='children.attachment.results') | flatten }}"
loop_control:
loop_var: attachment
In all cases, you can access history from the included file with attachment.history
Note also the second solution will let you use the default item
loop var in any of your included tasks if needed as the include loop var will not conflict with it.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论