

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 在中使用循环结构 AWSTOE
<a name="toe-looping-constructs"></a>

本部分提供了有助于您在 AWSTOE中创建循环结构的信息。循环结构定义了重复的指令序列。您可以在 AWSTOE中使用以下类型的循环结构：
+ `for` 结构 – 在有界的整数序列上迭代。
+ `forEach` 结构
  + `forEach` 使用输入列表循环 – 迭代有限的字符串集合。
  + `forEach` 带分隔列表的循环 – 迭代由分隔符连接的有限字符串集合。

**注意**  
循环结构仅支持字符串数据类型。

**Topics**
+ [引用迭代变量](#toe-loop-iteration-variables)
+ [循环结构的类型](#toe-loop-types)
+ [步骤字段](#toe-loop-step-fields)
+ [步骤和迭代输出](#toe-loop-step-output)

## 引用迭代变量
<a name="toe-loop-iteration-variables"></a>

要引用当前迭代变量的索引和值，必须在包含循环结构的步骤的输入主体中使用引用表达式 `{{ loop.* }}`。此表达式不能用于引用另一个步骤的循环结构的迭代变量。

引用表达式由以下成员组成：
+ `{{ loop.index }}` – 当前迭代的序数位置，索引为 `0`。
+ `{{ loop.value }}` – 与当前迭代变量关联的值。

### 循环名称
<a name="toe-loop-iteration-variables-names"></a>

 所有循环结构都有一个用于标识的可选名称字段。如果提供了循环名称，则可以使用它来引用步骤输入主体中的迭代变量。要引用命名循环的迭代索引和值，请在步骤的输入主体中使用带 `{{ loop.* }}` 的 `{{ <loop_name>.* }}`。此表达式不能用于引用另一个步骤的命名循环结构。

引用表达式由以下成员组成：
+ `{{ <loop_name>.index }}` – 命名循环当前迭代的序数位置，其索引位于 `0`。
+ `{{ <loop_name>.value }}` – 与命名循环的当前迭代变量关联的值。

### 解析引用表达式
<a name="toe-loop-iteration-variables-expressions"></a>

解 AWSTOE 析引用表达式如下：
+ `{{ <loop_name>.* }}`— 使用以下逻辑 AWSTOE 解析此表达式：
  + 如果当前正在运行的步骤的循环与 `<loop_name>` 值匹配，则引用表达式将解析为当前正在运行的步骤的循环结构。
  + 如果命名的循环结构出现在当前运行的步骤内，则 `<loop_name>` 解析为该结构。
+ `{{ loop.* }}`— 使用在当前运行的步骤中定义的循环结构 AWSTOE 解析表达式。

如果在不包含循环的步骤中使用引用表达式，则 AWSTOE 不会解析这些表达式，并且它们出现在步骤中而不进行替换。

**注意**  
引用表达式必须用双引号括起来，YAML 编译器才能正确解释。

## 循环结构的类型
<a name="toe-loop-types"></a>

本节提供有关可在 AWSTOE中使用的循环结构类型的信息和示例。

**Topics**
+ [`for` 循环](#toe-loop-types-for)
+ [`forEach` 使用输入列表循环](#toe-loop-types-foreach)
+ [`forEach` 使用带分隔列表的循环](#toe-loop-types-foreach-delimited)

### `for` 循环
<a name="toe-loop-types-for"></a>

该 `for` 循环迭代处于由变量开头和结尾勾勒的边界内指定的整数范围。迭代值在集合 `[start, end]` 中，包括边界值。

AWSTOE 验证`start``end`、和`updateBy`值，以确保组合不会导致无限循环。

`for` 循环架构

```
  - name: "StepName"
    action: "ActionModule"
    loop:
      name: "string"
      for:
        start: int
        end: int
        updateBy: int
inputs:
  ...
```


**`for` 循环输入**  

| 字段 | 描述 | Type | 必需 | 默认 | 
| --- | --- | --- | --- | --- | 
|  `name`  | 循环的唯一名称。与同一阶段的其他循环名称相比，它必须是唯一的。 |  字符串  |  否  |  ""  | 
|  `start`  | 迭代的起始值。不接受链接表达式。 |  整数  |  是  |  不适用  | 
| `end` | 迭代的结束值。不接受链接表达式。 | 整数 | 是 | 不适用 | 
| `updateBy` | 通过加法更新迭代值的差异。它必须是非零负值或正值。不接受链接表达式。 | 整数 | 是 | 不适用 | 

`for` 循环输入示例

```
  - name: "CalculateFileUploadLatencies"
    action: "ExecutePowerShell"
    loop:
      for:
        start: 100000
        end: 1000000
        updateBy: 100000
    inputs:
      commands:
        - |
          $f = new-object System.IO.FileStream c:\temp\test{{ loop.index }}.txt, Create, ReadWrite
          $f.SetLength({{ loop.value }}MB)
          $f.Close()
        - c:\users\administrator\downloads\latencyTest.exe --file c:\temp\test{{ loop.index }}.txt
        - AWS s3 cp c:\users\administrator\downloads\latencyMetrics.json s3://bucket/latencyMetrics.json
        - |
          Remove-Item -Path c:\temp\test{{ loop.index }}.txt
          Remove-Item -Path c:\users\administrator\downloads\latencyMetrics.json
```

### `forEach` 使用输入列表循环
<a name="toe-loop-types-foreach"></a>

该 `forEach` 循环迭代一个显式的值列表，该列表可以是字符串和链式表达式。

`forEach` 使用输入列表架构进行循环

```
  - name: "StepName"
    action: "ActionModule"
    loop:
      name: "string"
      forEach:
        - "string"
    inputs:
  ...
```


**`forEach` 使用输入列表输入进行循环**  

| 字段 | 描述 | Type | 必需 | 默认 | 
| --- | --- | --- | --- | --- | 
|  `name`  | 循环的唯一名称。与同一阶段的其他循环名称相比，它必须是唯一的。 |  字符串  |  否  |  ""  | 
|  `forEach` 循环字符串列表  |  用于迭代的字符串列表。接受链式表达式作为列表中的字符串。链式表达式必须用双引号括起来，YAML 编译器才能正确解释它们。  |  字符串列表  |  是  |  不适用  | 

`forEach` 使用输入列表循环示例 1

```
  - name: "ExecuteCustomScripts"
    action: "ExecuteBash"
    loop:
      name: BatchExecLoop
      forEach:
        - /tmp/script1.sh
        - /tmp/script2.sh
        - /tmp/script3.sh
    inputs:
      commands:
        - echo "Count {{ BatchExecLoop.index }}"
        - sh "{{ loop.value }}"
        - |
          retVal=$?
          if [ $retVal -ne 0 ]; then
            echo "Failed"
          else
            echo "Passed"
         fi
```

`forEach` 使用输入列表循环示例 2

```
  - name: "RunMSIWithDifferentArgs"
    action: "ExecuteBinary"
    loop:
      name: MultiArgLoop
      forEach:
        - "ARG1=C:\Users ARG2=1"
        - "ARG1=C:\Users"
        - "ARG1=C:\Users ARG3=C:\Users\Administrator\Documents\f1.txt"
    inputs:
      commands:
        path: "c:\users\administrator\downloads\runner.exe"
        args:
          - "{{ MultiArgLoop.value }}"
```

`forEach` 使用输入列表循环示例 3

```
  - name: "DownloadAllBinaries"
    action: "S3Download"
    loop:
      name: MultiArgLoop
      forEach:
        - "bin1.exe"
        - "bin10.exe"
        - "bin5.exe"
    inputs:
      - source: "s3://bucket/{{ loop.value }}"
        destination: "c:\temp\{{ loop.value }}"
```

### `forEach` 使用带分隔列表的循环
<a name="toe-loop-types-foreach-delimited"></a>

该循环遍历包含由分隔符分隔的值的字符串。要遍历字符串的组成部分，请 AWSTOE 使用分隔符将字符串拆分为适合迭代的数组。

`forEach` 使用分隔列表架构的循环

```
  - name: "StepName"
    action: "ActionModule"
    loop:
      name: "string"
      forEach:
        list: "string"
        delimiter: ".,;:\n\t -_"
    inputs:
  ...
```


**`forEach` 使用分隔列表输入的循环**  

| 字段 | 描述 | Type | 必需 | 默认 | 
| --- | --- | --- | --- | --- | 
|  `name`  | 赋予循环的唯一名称。与同一阶段的其他循环名称相比，它应该是唯一的。 |  字符串  |  否  |  ""  | 
|  `list`  | 由组成字符串组成的字符串，这些字符串由一个通用分隔符连接在一起。也接受链式表达式。如果是链式表达式，请确保用双引号将它们括起来，以便 YAML 编译器正确解释。 | 字符串 |  是  |  不适用  | 
| `delimiter` | 用于在区块中分隔字符串的字符。默认为逗号字符。在给定的列表中只允许使用一个分隔符：[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/imagebuilder/latest/userguide/toe-looping-constructs.html) 不能使用链接表达式。 | 字符串 | 否 | 逗号："," | 

**注意**  
`list` 的值被视为不可变的字符串。如果在运行时更改了 `list` 的源，则它在运行期间不会反映出来。

`forEach` 使用分隔列表循环示例 1

此示例使用以下链接表达式模式来引用另一个步骤的输出：`<phase_name>.<step_name>.[inputs | outputs].<var_name>`。

```
  - name: "RunMSIs"
    action: "ExecuteBinary"
    loop:
      forEach:
        list: "{{ build.GetAllMSIPathsForInstallation.outputs.stdout }}"
        delimiter: "\n"
    inputs:
      commands:
        path: "{{ loop.value }}"
```

`forEach` 使用分隔列表循环示例 2

```
  - name: "UploadMetricFiles"
    action: "S3Upload"
    loop:
      forEach:
        list: "/tmp/m1.txt,/tmp/m2.txt,/tmp/m3.txt,..."
    inputs:
      commands:
        - source: "{{ loop.value }}"
          destination: "s3://bucket/key/{{ loop.value }}"
```

## 步骤字段
<a name="toe-loop-step-fields"></a>

循环是步骤的一部分。任何与步骤运行相关的字段都不会应用于单个迭代。步骤字段仅应用于步骤级别，如下所示：
+ *timeoutSeconds* – 循环的所有迭代都必须在此字段指定的时间段内运行。如果循环运行超时，则 AWSTOE 运行该步骤的重试策略，并为每次新的尝试重置超时参数。如果循环运行在达到最大重试次数后超过超时值，则该步骤的失败消息将表明循环运行已超时。
+ *onFailure* – 对步骤应用失败处理，如下所示：
  + 如果 *onFa* ilure 设置为`Abort`，则 AWSTOE 退出循环并根据重试策略重试该步骤。在达到最大重试次数后，将当前步骤 AWSTOE 标记为失败，并停止运行该进程。

    AWSTOE 将父阶段和文档的状态码设置为`Failed`。
**注意**  
在失败的步骤之后，不再运行任何其他步骤。
  + 如果 *onFailure* 设置为 `Continue`，则 AWSTOE 退出循环并根据重试策略重试该步骤。在达到最大重试次数后，将当前步骤 AWSTOE 标记为失败，然后继续运行下一个步骤。

    AWSTOE 将父阶段和文档的状态码设置为`Failed`。
  + 如果 *onFailure* 设置为 `Ignore`，则 AWSTOE 退出循环并根据重试策略重试该步骤。在达到最大重试次数后，将当前步骤 AWSTOE 标记为`IgnoredFailure`，然后继续运行下一步。

    AWSTOE 将父阶段和文档的状态码设置为`SuccessWithIgnoredFailure`。
**注意**  
这仍被视为成功运行，但包含一些信息，可让您知道一个或多个步骤失败并被忽略。
+ *maxAttempts * – 每次重试，整个步骤和所有迭代都是从头开始运行的。
+ *状态* – 步骤运行的总体状态。`status` 不代表各个迭代的状态。包含循环的步骤状态确定如下：
  + 如果单个迭代运行失败，则步骤的状态指向失败。
  + 如果所有迭代都成功，则步骤的状态指向成功。
+ *startTime * – 步骤运行的总开始时间。不代表各个迭代的开始时间。
+ *endTime * – 步骤运行的总结束时间。不代表单个迭代的结束时间。
+ *failureMessage * – 包括在出现非超时错误时失败的迭代索引。如果出现超时错误，消息指出该循环运行失败。为了最大限度地减少失败消息的大小，不为每次迭代提供单独的错误消息。

## 步骤和迭代输出
<a name="toe-loop-step-output"></a>

每次迭代都包含一个输出。在循环运行结束时， AWSTOE 将所有成功的迭代输出合并到中。`detailedOutput.json`合并输出是属于相应输出键的值的排序规则，这些值在操作模块的输出架构中定义。下面的示例说明如何合并输出：

**迭代 1 的 `ExecuteBash` 的输出**

```
{
	"stdout":"Hello"
}
```

**迭代 2 的 `ExecuteBash` 的输出**

```
{
	"stdout":"World"
}
```

**步骤的 `ExecuteBash` 的输出**

```
{
	"stdout":"Hello\nWorld"
}
```

例如，`ExecuteBash`、`ExecutePowerShell` 和 `ExecuteBinary` 是操作模块，其返回 `STDOUT` 作为操作模块输出。`STDOUT` 消息与换行符连接以在 `detailedOutput.json` 中生成步骤的总体输出。

AWSTOE 不会合并失败迭代的输出。